💥 (admin): Exige un permiso propio en cada página
This commit is contained in:
parent
2299e63585
commit
f337119219
6 changed files with 31 additions and 79 deletions
|
|
@ -88,7 +88,6 @@ impl ActionBag {
|
||||||
/// key: "tools".to_owned(),
|
/// key: "tools".to_owned(),
|
||||||
/// path: "/admin/tools".to_owned(),
|
/// path: "/admin/tools".to_owned(),
|
||||||
/// title: Lc::n("Tools"),
|
/// title: Lc::n("Tools"),
|
||||||
/// permission: None,
|
|
||||||
/// weight: 60,
|
/// weight: 60,
|
||||||
/// });
|
/// });
|
||||||
/// }
|
/// }
|
||||||
|
|
@ -130,10 +129,18 @@ impl DeclareAdminSections {
|
||||||
/// # Ejemplo
|
/// # Ejemplo
|
||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// use pagetop::locale::Lc;
|
/// use pagetop::prelude::*;
|
||||||
/// use pagetop_admin::action::{DeclareAdminPages, PageBag};
|
/// use pagetop_admin::action::{DeclareAdminPages, PageBag};
|
||||||
/// use pagetop_admin::registry::{AdminPage, AdminPageKind};
|
/// use pagetop_admin::registry::{AdminPage, AdminPageKind};
|
||||||
///
|
///
|
||||||
|
/// struct ExportPermission;
|
||||||
|
///
|
||||||
|
/// impl Permission for ExportPermission {
|
||||||
|
/// fn key(&self) -> CowStr {
|
||||||
|
/// "tools:export".into()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
/// fn declare_pages(bag: &mut PageBag) {
|
/// fn declare_pages(bag: &mut PageBag) {
|
||||||
/// bag.add(AdminPage {
|
/// bag.add(AdminPage {
|
||||||
/// path: "/admin/tools/export".to_owned(),
|
/// path: "/admin/tools/export".to_owned(),
|
||||||
|
|
@ -141,7 +148,7 @@ impl DeclareAdminSections {
|
||||||
/// title: Lc::n("Export"),
|
/// title: Lc::n("Export"),
|
||||||
/// description: Some(Lc::n("Export site data.")),
|
/// description: Some(Lc::n("Export site data.")),
|
||||||
/// weight: 0,
|
/// weight: 0,
|
||||||
/// permission: None,
|
/// permission: &ExportPermission,
|
||||||
/// kind: AdminPageKind::View,
|
/// kind: AdminPageKind::View,
|
||||||
/// });
|
/// });
|
||||||
/// }
|
/// }
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,6 @@ fn render_sections(cx: &Context, candidates: &[&'static AdminSection]) -> Option
|
||||||
let sections_with_pages: Vec<_> = candidates
|
let sections_with_pages: Vec<_> = candidates
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.filter(|section| section.is_visible(cx))
|
|
||||||
.map(|section| {
|
.map(|section| {
|
||||||
let pages: Vec<_> = reg
|
let pages: Vec<_> = reg
|
||||||
.pages_for_section(§ion.key)
|
.pages_for_section(§ion.key)
|
||||||
|
|
@ -131,7 +130,7 @@ pub async fn config_form_get(request: HttpRequest) -> Result<Markup, ErrorPage>
|
||||||
return Err(ErrorPage::NotFound(Some(request.clone())));
|
return Err(ErrorPage::NotFound(Some(request.clone())));
|
||||||
};
|
};
|
||||||
|
|
||||||
require_permission(&request, page.permission_key())?;
|
require_permission(&request, page.permission)?;
|
||||||
|
|
||||||
let title = page.title.clone();
|
let title = page.title.clone();
|
||||||
let mut form = ConfigForm::with_schema(schema.clone());
|
let mut form = ConfigForm::with_schema(schema.clone());
|
||||||
|
|
@ -170,7 +169,7 @@ pub async fn config_form_post(
|
||||||
return Err(ErrorPage::NotFound(Some(request.clone())));
|
return Err(ErrorPage::NotFound(Some(request.clone())));
|
||||||
};
|
};
|
||||||
|
|
||||||
require_permission(&request, page.permission_key())?;
|
require_permission(&request, page.permission)?;
|
||||||
|
|
||||||
let mut save_error = false;
|
let mut save_error = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ fn declare_pages(bag: &mut PageBag) {
|
||||||
title: Lc::n("My App"),
|
title: Lc::n("My App"),
|
||||||
description: Some(Lc::n("Configure My App.")),
|
description: Some(Lc::n("Configure My App.")),
|
||||||
weight: 0,
|
weight: 0,
|
||||||
permission: Some(&MyPermission::Config),
|
permission: &MyPermission::Config,
|
||||||
kind: AdminPageKind::View,
|
kind: AdminPageKind::View,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -63,7 +63,7 @@ enum MyPermission {
|
||||||
impl Permission for MyPermission {
|
impl Permission for MyPermission {
|
||||||
fn key(&self) -> CowStr {
|
fn key(&self) -> CowStr {
|
||||||
match self {
|
match self {
|
||||||
Self::Config => "myapp.config".into(),
|
Self::Config => "myapp:config".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,38 +14,12 @@ use crate::action::{
|
||||||
};
|
};
|
||||||
use crate::settings::SettingsSchema;
|
use crate::settings::SettingsSchema;
|
||||||
|
|
||||||
// **< AdminPermission >****************************************************************************
|
|
||||||
|
|
||||||
/// Permisos propios de `pagetop-admin`.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum AdminPermission {
|
|
||||||
/// Acceso por defecto a una página de administración que no declara un permiso propio.
|
|
||||||
Access,
|
|
||||||
/// Acceso a la sección integrada "people".
|
|
||||||
AccessPeople,
|
|
||||||
/// Acceso a la sección integrada "structure".
|
|
||||||
AccessStructure,
|
|
||||||
/// Acceso a la sección integrada "config".
|
|
||||||
AccessConfig,
|
|
||||||
/// Acceso a la sección integrada "reports".
|
|
||||||
AccessReports,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Permission for AdminPermission {
|
|
||||||
fn key(&self) -> CowStr {
|
|
||||||
match self {
|
|
||||||
Self::Access => "admin:access".into(),
|
|
||||||
Self::AccessPeople => "admin.access_people".into(),
|
|
||||||
Self::AccessStructure => "admin.access_structure".into(),
|
|
||||||
Self::AccessConfig => "admin.access_config".into(),
|
|
||||||
Self::AccessReports => "admin.access_reports".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// **< Tipos del registro >*************************************************************************
|
// **< Tipos del registro >*************************************************************************
|
||||||
|
|
||||||
/// Sección del panel de administración (agrupación en el sidebar).
|
/// Sección del panel de administración (agrupación de páginas en el menú y en el *dashboard*).
|
||||||
|
///
|
||||||
|
/// Una sección no tiene permiso propio: se muestra a quien pueda acceder a alguna de sus páginas, y
|
||||||
|
/// se oculta si no le queda ninguna.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AdminSection {
|
pub struct AdminSection {
|
||||||
/// Identificador único de la sección (p. ej. `"config"`).
|
/// Identificador único de la sección (p. ej. `"config"`).
|
||||||
|
|
@ -54,24 +28,10 @@ pub struct AdminSection {
|
||||||
pub path: String,
|
pub path: String,
|
||||||
/// Título visible en el sidebar.
|
/// Título visible en el sidebar.
|
||||||
pub title: Lc,
|
pub title: Lc,
|
||||||
/// Permiso requerido para ver la sección (`None` = siempre visible).
|
|
||||||
pub permission: Option<PermissionRef>,
|
|
||||||
/// Peso para ordenar en el sidebar (menor = antes).
|
/// Peso para ordenar en el sidebar (menor = antes).
|
||||||
pub weight: i32,
|
pub weight: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AdminSection {
|
|
||||||
/// Devuelve `true` si el usuario actual puede ver esta sección.
|
|
||||||
pub fn is_visible(&self, cx: &Context) -> bool {
|
|
||||||
match self.permission {
|
|
||||||
None => true,
|
|
||||||
Some(permission) => cx
|
|
||||||
.request()
|
|
||||||
.is_some_and(|request| has_permission(request, permission)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Página del panel de administración.
|
/// Página del panel de administración.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AdminPage {
|
pub struct AdminPage {
|
||||||
|
|
@ -85,22 +45,18 @@ pub struct AdminPage {
|
||||||
pub description: Option<Lc>,
|
pub description: Option<Lc>,
|
||||||
/// Peso dentro de la sección (menor = antes).
|
/// Peso dentro de la sección (menor = antes).
|
||||||
pub weight: i32,
|
pub weight: i32,
|
||||||
/// Permiso requerido para acceder (`None` = requiere [`AdminPermission::Access`]).
|
/// Permiso requerido para acceder. Es obligatorio: cada página declara el suyo, y es también
|
||||||
pub permission: Option<PermissionRef>,
|
/// el que decide si su sección se muestra.
|
||||||
|
pub permission: PermissionRef,
|
||||||
/// Tipo de página y datos asociados.
|
/// Tipo de página y datos asociados.
|
||||||
pub kind: AdminPageKind,
|
pub kind: AdminPageKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AdminPage {
|
impl AdminPage {
|
||||||
/// Permiso efectivo: el declarado, o [`AdminPermission::Access`] si no se especificó ninguno.
|
|
||||||
pub fn permission_key(&self) -> PermissionRef {
|
|
||||||
self.permission.unwrap_or(&AdminPermission::Access)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Devuelve `true` si el usuario actual puede acceder a esta página.
|
/// Devuelve `true` si el usuario actual puede acceder a esta página.
|
||||||
pub fn is_accessible(&self, cx: &Context) -> bool {
|
pub fn is_accessible(&self, cx: &Context) -> bool {
|
||||||
cx.request()
|
cx.request()
|
||||||
.is_some_and(|request| has_permission(request, self.permission_key()))
|
.is_some_and(|request| has_permission(request, self.permission))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,9 +233,7 @@ pub fn global() -> &'static AdminRegistry {
|
||||||
pub fn can_access_admin(cx: &Context) -> bool {
|
pub fn can_access_admin(cx: &Context) -> bool {
|
||||||
let reg = global();
|
let reg = global();
|
||||||
reg.ordered_sections().into_iter().any(|section| {
|
reg.ordered_sections().into_iter().any(|section| {
|
||||||
section.is_visible(cx)
|
reg.pages_for_section(§ion.key)
|
||||||
&& reg
|
|
||||||
.pages_for_section(§ion.key)
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.any(|page| page.is_accessible(cx))
|
.any(|page| page.is_accessible(cx))
|
||||||
})
|
})
|
||||||
|
|
@ -306,9 +260,6 @@ pub fn admin_navbar(cx: &Context) -> Navbar {
|
||||||
));
|
));
|
||||||
|
|
||||||
for section in reg.ordered_sections() {
|
for section in reg.ordered_sections() {
|
||||||
if !section.is_visible(cx) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let pages: Vec<_> = reg
|
let pages: Vec<_> = reg
|
||||||
.pages_for_section(§ion.key)
|
.pages_for_section(§ion.key)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use pagetop::prelude::*;
|
||||||
use crate::ADMIN_BASE_PATH;
|
use crate::ADMIN_BASE_PATH;
|
||||||
use crate::LOCALES_ADMIN;
|
use crate::LOCALES_ADMIN;
|
||||||
use crate::action::SectionBag;
|
use crate::action::SectionBag;
|
||||||
use crate::registry::{AdminPermission, AdminSection};
|
use crate::registry::AdminSection;
|
||||||
|
|
||||||
/// Declara las secciones incorporadas del panel de administración.
|
/// Declara las secciones incorporadas del panel de administración.
|
||||||
///
|
///
|
||||||
|
|
@ -16,35 +16,30 @@ pub(crate) fn declare_default_sections(bag: &mut SectionBag) {
|
||||||
key: "people".to_owned(),
|
key: "people".to_owned(),
|
||||||
path: format!("{}/people", base),
|
path: format!("{}/people", base),
|
||||||
title: Lc::t("section-people", &LOCALES_ADMIN),
|
title: Lc::t("section-people", &LOCALES_ADMIN),
|
||||||
permission: Some(&AdminPermission::AccessPeople),
|
|
||||||
weight: 10,
|
weight: 10,
|
||||||
});
|
});
|
||||||
bag.add(AdminSection {
|
bag.add(AdminSection {
|
||||||
key: "structure".to_owned(),
|
key: "structure".to_owned(),
|
||||||
path: format!("{}/structure", base),
|
path: format!("{}/structure", base),
|
||||||
title: Lc::t("section-structure", &LOCALES_ADMIN),
|
title: Lc::t("section-structure", &LOCALES_ADMIN),
|
||||||
permission: Some(&AdminPermission::AccessStructure),
|
|
||||||
weight: 20,
|
weight: 20,
|
||||||
});
|
});
|
||||||
bag.add(AdminSection {
|
bag.add(AdminSection {
|
||||||
key: "config".to_owned(),
|
key: "config".to_owned(),
|
||||||
path: format!("{}/config", base),
|
path: format!("{}/config", base),
|
||||||
title: Lc::t("section-config", &LOCALES_ADMIN),
|
title: Lc::t("section-config", &LOCALES_ADMIN),
|
||||||
permission: Some(&AdminPermission::AccessConfig),
|
|
||||||
weight: 30,
|
weight: 30,
|
||||||
});
|
});
|
||||||
bag.add(AdminSection {
|
bag.add(AdminSection {
|
||||||
key: "reports".to_owned(),
|
key: "reports".to_owned(),
|
||||||
path: format!("{}/reports", base),
|
path: format!("{}/reports", base),
|
||||||
title: Lc::t("section-reports", &LOCALES_ADMIN),
|
title: Lc::t("section-reports", &LOCALES_ADMIN),
|
||||||
permission: Some(&AdminPermission::AccessReports),
|
|
||||||
weight: 40,
|
weight: 40,
|
||||||
});
|
});
|
||||||
bag.add(AdminSection {
|
bag.add(AdminSection {
|
||||||
key: "help".to_owned(),
|
key: "help".to_owned(),
|
||||||
path: format!("{}/help", base),
|
path: format!("{}/help", base),
|
||||||
title: Lc::t("section-help", &LOCALES_ADMIN),
|
title: Lc::t("section-help", &LOCALES_ADMIN),
|
||||||
permission: None,
|
|
||||||
weight: 50,
|
weight: 50,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ fn declare_admin_pages(bag: &mut PageBag) {
|
||||||
title: Lc::t("title-admin-users", &LOCALES_USER),
|
title: Lc::t("title-admin-users", &LOCALES_USER),
|
||||||
description: Some(Lc::t("description-admin-users", &LOCALES_USER)),
|
description: Some(Lc::t("description-admin-users", &LOCALES_USER)),
|
||||||
weight: 0,
|
weight: 0,
|
||||||
permission: Some(&permission::UserPermission::AdminUsers),
|
permission: &permission::UserPermission::AdminUsers,
|
||||||
kind: AdminPageKind::View,
|
kind: AdminPageKind::View,
|
||||||
});
|
});
|
||||||
bag.add(AdminPage {
|
bag.add(AdminPage {
|
||||||
|
|
@ -164,7 +164,7 @@ fn declare_admin_pages(bag: &mut PageBag) {
|
||||||
title: Lc::t("title-admin-roles", &LOCALES_USER),
|
title: Lc::t("title-admin-roles", &LOCALES_USER),
|
||||||
description: Some(Lc::t("description-admin-roles", &LOCALES_USER)),
|
description: Some(Lc::t("description-admin-roles", &LOCALES_USER)),
|
||||||
weight: 10,
|
weight: 10,
|
||||||
permission: Some(&permission::UserPermission::AdminRoles),
|
permission: &permission::UserPermission::AdminRoles,
|
||||||
kind: AdminPageKind::View,
|
kind: AdminPageKind::View,
|
||||||
});
|
});
|
||||||
bag.add(AdminPage {
|
bag.add(AdminPage {
|
||||||
|
|
@ -173,7 +173,7 @@ fn declare_admin_pages(bag: &mut PageBag) {
|
||||||
title: Lc::t("title-admin-permissions", &LOCALES_USER),
|
title: Lc::t("title-admin-permissions", &LOCALES_USER),
|
||||||
description: Some(Lc::t("description-admin-permissions", &LOCALES_USER)),
|
description: Some(Lc::t("description-admin-permissions", &LOCALES_USER)),
|
||||||
weight: 20,
|
weight: 20,
|
||||||
permission: Some(&permission::UserPermission::AdminPermissions),
|
permission: &permission::UserPermission::AdminPermissions,
|
||||||
kind: AdminPageKind::View,
|
kind: AdminPageKind::View,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue