web synchronized with the new changes
This commit is contained in:
106
web/src/authz.rs
Normal file
106
web/src/authz.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
use crate::auth::{AuthorizationSnapshot, Permission};
|
||||
|
||||
pub(crate) const STRUCT_PROFILE: &str = "struct:profile";
|
||||
pub(crate) const STRUCT_TABLE: &str = "struct:table";
|
||||
pub(crate) const STRUCT_SCRIPT: &str = "struct:script";
|
||||
pub(crate) const STRUCT_VALIDATION: &str = "struct:validation";
|
||||
pub(crate) const STRUCT_ROLE: &str = "struct:role";
|
||||
pub(crate) const STRUCT_USER: &str = "struct:user";
|
||||
pub(crate) const MANAGE: &str = "manage";
|
||||
|
||||
pub(crate) fn permits(snapshot: &AuthorizationSnapshot, object: &str, action: &str) -> bool {
|
||||
permissions_permit(&snapshot.permissions, object, action)
|
||||
}
|
||||
|
||||
pub(crate) fn permissions_permit(
|
||||
permissions: &[Permission],
|
||||
object: &str,
|
||||
action: &str,
|
||||
) -> bool {
|
||||
permissions
|
||||
.iter()
|
||||
.any(|permission| permission.action == action && object_matches(&permission.object, object))
|
||||
}
|
||||
|
||||
pub(crate) fn can_manage(snapshot: &AuthorizationSnapshot, area: &str) -> bool {
|
||||
permits(snapshot, area, MANAGE)
|
||||
}
|
||||
|
||||
pub(crate) fn can_open_admin(snapshot: &AuthorizationSnapshot) -> bool {
|
||||
[
|
||||
STRUCT_PROFILE,
|
||||
STRUCT_TABLE,
|
||||
STRUCT_SCRIPT,
|
||||
STRUCT_VALIDATION,
|
||||
STRUCT_ROLE,
|
||||
STRUCT_USER,
|
||||
]
|
||||
.into_iter()
|
||||
.any(|area| can_manage(snapshot, area))
|
||||
}
|
||||
|
||||
pub(crate) fn table_object(profile: &str, table: &str) -> String {
|
||||
format!("data:{profile}/{table}")
|
||||
}
|
||||
|
||||
pub(crate) fn permits_table(
|
||||
snapshot: &AuthorizationSnapshot,
|
||||
profile: &str,
|
||||
table: &str,
|
||||
action: &str,
|
||||
) -> bool {
|
||||
permits(snapshot, &table_object(profile, table), action)
|
||||
}
|
||||
|
||||
pub(crate) fn is_direct_permission(permissions: &[Permission], object: &str, action: &str) -> bool {
|
||||
permissions
|
||||
.iter()
|
||||
.any(|permission| permission.object == object && permission.action == action)
|
||||
}
|
||||
|
||||
fn object_matches(pattern: &str, object: &str) -> bool {
|
||||
match pattern.strip_suffix('*') {
|
||||
Some(prefix) => object.starts_with(prefix),
|
||||
None => pattern == object,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn snapshot(permissions: &[(&str, &str)]) -> AuthorizationSnapshot {
|
||||
AuthorizationSnapshot {
|
||||
role: "bookkeeper".to_string(),
|
||||
permissions: permissions
|
||||
.iter()
|
||||
.map(|(object, action)| Permission {
|
||||
object: (*object).to_string(),
|
||||
action: (*action).to_string(),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn structural_access_comes_from_permissions_not_role_names() {
|
||||
let authorization = snapshot(&[(STRUCT_TABLE, MANAGE)]);
|
||||
assert!(can_manage(&authorization, STRUCT_TABLE));
|
||||
assert!(can_open_admin(&authorization));
|
||||
assert!(!can_manage(&authorization, STRUCT_ROLE));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_wildcards_match_the_server_object_shapes() {
|
||||
let global = snapshot(&[("data:*", "read")]);
|
||||
assert!(permits_table(&global, "acme", "invoice", "read"));
|
||||
|
||||
let profile = snapshot(&[("data:acme/*", "insert")]);
|
||||
assert!(permits_table(&profile, "acme", "invoice", "insert"));
|
||||
assert!(!permits_table(&profile, "other", "invoice", "insert"));
|
||||
|
||||
let table = snapshot(&[("data:acme/invoice", "delete")]);
|
||||
assert!(permits_table(&table, "acme", "invoice", "delete"));
|
||||
assert!(!permits_table(&table, "acme", "customer", "delete"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user