use super::ResourcePerms;
#[derive(Debug, PartialEq, Eq)]
pub(super) enum Access {
Granted,
DeniedDashboard,
DeniedResource,
}
pub(super) enum CollectionAction {
List,
Create,
Bulk,
}
pub(super) enum MemberAction {
Detail,
Edit,
Delete,
ResetPassword,
}
impl CollectionAction {
pub(super) fn parse_get(action: &str) -> Option<Self> {
match action {
"list" => Some(Self::List),
"create" => Some(Self::Create),
"bulk" => Some(Self::Bulk),
_ => None,
}
}
pub(super) fn parse_post(action: &str) -> Option<Self> {
match action {
"create" => Some(Self::Create),
"bulk" => Some(Self::Bulk),
_ => None,
}
}
pub(super) fn authorize_get(&self, perms: &ResourcePerms) -> Access {
match self {
Self::List => {
if perms.can_read {
Access::Granted
} else {
Access::DeniedDashboard
}
}
Self::Create => {
if !perms.can_read {
Access::DeniedDashboard
} else if perms.can_create {
Access::Granted
} else {
Access::DeniedResource
}
}
Self::Bulk => {
if perms.can_update {
Access::Granted
} else {
Access::DeniedResource
}
}
}
}
pub(super) fn authorize_post(&self, perms: &ResourcePerms, bulk_action: &str) -> Access {
if !perms.can_create {
return Access::DeniedResource;
}
match self {
Self::Create => Access::Granted,
Self::Bulk => {
let can_bulk = if bulk_action == "delete" {
perms.can_delete
} else {
perms.can_update
};
if can_bulk {
Access::Granted
} else {
Access::DeniedResource
}
}
Self::List => Access::DeniedResource,
}
}
}
impl MemberAction {
pub(super) fn parse_get(action: &str) -> Option<Self> {
match action {
"detail" => Some(Self::Detail),
"edit" => Some(Self::Edit),
"delete" => Some(Self::Delete),
_ => None,
}
}
pub(super) fn parse_post(action: &str) -> Option<Self> {
match action {
"edit" => Some(Self::Edit),
"delete" => Some(Self::Delete),
"reset-password" => Some(Self::ResetPassword),
_ => None,
}
}
pub(super) fn authorize(&self, perms: &ResourcePerms, owns: bool) -> Access {
match self {
Self::Detail => Access::Granted,
Self::Edit | Self::ResetPassword => grant_if(perms.can_edit(owns)),
Self::Delete => grant_if(perms.can_remove(owns)),
}
}
}
fn grant_if(allowed: bool) -> Access {
if allowed {
Access::Granted
} else {
Access::DeniedResource
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::admin::permissions::{Groupe, Permission};
use crate::auth::session::CurrentUser;
fn perms(c: bool, r: bool, u: bool, d: bool, uo: bool, dorm: bool) -> ResourcePerms {
ResourcePerms {
can_create: c,
can_read: r,
can_update: u,
can_delete: d,
can_update_own: uo,
can_delete_own: dorm,
}
}
const NONE: ResourcePerms = ResourcePerms {
can_create: false,
can_read: false,
can_update: false,
can_delete: false,
can_update_own: false,
can_delete_own: false,
};
#[test]
fn collection_parse_rejects_cross_method() {
assert!(CollectionAction::parse_get("list").is_some());
assert!(CollectionAction::parse_post("list").is_none()); assert!(CollectionAction::parse_get("nope").is_none());
assert!(CollectionAction::parse_post("create").is_some());
}
#[test]
fn member_parse_rejects_cross_method() {
assert!(MemberAction::parse_get("detail").is_some());
assert!(MemberAction::parse_post("detail").is_none()); assert!(MemberAction::parse_get("reset-password").is_none()); assert!(MemberAction::parse_post("reset-password").is_some());
}
#[test]
fn list_requires_read() {
assert_eq!(
CollectionAction::List.authorize_get(&perms(false, true, false, false, false, false)),
Access::Granted
);
assert_eq!(
CollectionAction::List.authorize_get(&NONE),
Access::DeniedDashboard
);
}
#[test]
fn create_get_is_two_stage() {
assert_eq!(
CollectionAction::Create.authorize_get(&NONE),
Access::DeniedDashboard
);
assert_eq!(
CollectionAction::Create.authorize_get(&perms(false, true, false, false, false, false)),
Access::DeniedResource
);
assert_eq!(
CollectionAction::Create.authorize_get(&perms(true, true, false, false, false, false)),
Access::Granted
);
}
#[test]
fn bulk_get_requires_update_no_dashboard() {
assert_eq!(
CollectionAction::Bulk.authorize_get(&perms(false, false, true, false, false, false)),
Access::Granted
);
assert_eq!(
CollectionAction::Bulk.authorize_get(&NONE),
Access::DeniedResource
);
}
#[test]
fn create_post_requires_create() {
assert_eq!(
CollectionAction::Create
.authorize_post(&perms(true, false, false, false, false, false), ""),
Access::Granted
);
assert_eq!(
CollectionAction::Create.authorize_post(&NONE, ""),
Access::DeniedResource
);
}
#[test]
fn bulk_post_needs_create_plus_operation_right() {
assert_eq!(
CollectionAction::Bulk
.authorize_post(&perms(true, false, true, false, false, false), "activate"),
Access::Granted
);
assert_eq!(
CollectionAction::Bulk
.authorize_post(&perms(true, false, false, true, false, false), "delete"),
Access::Granted
);
assert_eq!(
CollectionAction::Bulk
.authorize_post(&perms(false, false, true, false, false, false), "activate"),
Access::DeniedResource
);
assert_eq!(
CollectionAction::Bulk
.authorize_post(&perms(true, false, true, false, false, false), "delete"),
Access::DeniedResource
);
}
#[test]
fn detail_always_granted_after_gate() {
assert_eq!(
MemberAction::Detail.authorize(&NONE, false),
Access::Granted
);
}
#[test]
fn edit_global_vs_own() {
assert_eq!(
MemberAction::Edit.authorize(&perms(false, false, true, false, false, false), false),
Access::Granted
);
assert_eq!(
MemberAction::Edit.authorize(&perms(false, false, false, false, true, false), true),
Access::Granted
);
assert_eq!(
MemberAction::Edit.authorize(&perms(false, false, false, false, true, false), false),
Access::DeniedResource
);
assert_eq!(
MemberAction::Edit.authorize(&NONE, true),
Access::DeniedResource
);
}
#[test]
fn delete_global_vs_own() {
assert_eq!(
MemberAction::Delete.authorize(&perms(false, false, false, true, false, false), false),
Access::Granted
);
assert_eq!(
MemberAction::Delete.authorize(&perms(false, false, false, false, false, true), true),
Access::Granted
);
assert_eq!(
MemberAction::Delete.authorize(&perms(false, false, false, false, false, true), false),
Access::DeniedResource
);
}
#[test]
fn reset_password_follows_edit_rule() {
assert_eq!(
MemberAction::ResetPassword
.authorize(&perms(false, false, true, false, false, false), false),
Access::Granted
);
assert_eq!(
MemberAction::ResetPassword
.authorize(&perms(false, false, false, false, true, false), true),
Access::Granted
);
assert_eq!(
MemberAction::ResetPassword
.authorize(&perms(false, false, false, true, false, false), true),
Access::DeniedResource
);
}
fn user(is_superuser: bool, groupes: Vec<Groupe>) -> CurrentUser {
CurrentUser {
id: crate::utils::pk::Pk::default(),
username: "tester".to_string(),
is_staff: true,
is_superuser,
groupes,
}
}
#[test]
fn resolve_superuser_grants_everything() {
let p = ResourcePerms::resolve(&user(true, vec![]), "anything");
assert!(
p.can_create
&& p.can_read
&& p.can_update
&& p.can_delete
&& p.can_update_own
&& p.can_delete_own
);
}
#[test]
fn resolve_reads_matching_permission() {
let mut perm = Permission::zeroed("blog".to_string());
perm.can_read = true;
perm.can_update = true;
let groupe = Groupe {
id: 1,
nom: "editors".to_string(),
permissions: vec![perm],
};
let p = ResourcePerms::resolve(&user(false, vec![groupe]), "blog");
assert!(p.can_read && p.can_update);
assert!(!p.can_create && !p.can_delete);
}
#[test]
fn resolve_unknown_resource_is_all_false() {
let p = ResourcePerms::resolve(&user(false, vec![]), "ghost");
assert_eq!(
(
p.can_create,
p.can_read,
p.can_update,
p.can_delete,
p.can_update_own,
p.can_delete_own
),
(false, false, false, false, false, false)
);
}
}