mirror of
https://gitlab.com/fabinfra/fabaccess/bffh.git
synced 2024-11-25 08:07:57 +01:00
Add Permissions back
This commit is contained in:
parent
541f8585c0
commit
eec6c3b60c
@ -1,6 +1,6 @@
|
|||||||
//! Authentication subsystem
|
//! Authentication subsystem
|
||||||
//!
|
//!
|
||||||
//! Authorization is over in `access.rs`
|
//! Authorization is over in `permissions`
|
||||||
//! Authentication using SASL
|
//! Authentication using SASL
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -8,7 +8,7 @@ use crate::error::Result;
|
|||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use std::net::{SocketAddr, IpAddr, ToSocketAddrs};
|
use std::net::{SocketAddr, IpAddr, ToSocketAddrs};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use crate::db::access::PermRule;
|
use crate::permissions::{PermRule, RoleIdentifier};
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
|
|
||||||
pub fn read(path: &Path) -> Result<Config> {
|
pub fn read(path: &Path) -> Result<Config> {
|
||||||
@ -41,13 +41,13 @@ pub struct Config {
|
|||||||
|
|
||||||
pub db_path: PathBuf,
|
pub db_path: PathBuf,
|
||||||
|
|
||||||
pub roles: HashMap<String, RoleConfig>,
|
pub roles: HashMap<RoleIdentifier, RoleConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct RoleConfig {
|
pub struct RoleConfig {
|
||||||
#[serde(default = "Vec::new")]
|
#[serde(default = "Vec::new")]
|
||||||
pub parents: Vec<String>,
|
pub parents: Vec<RoleIdentifier>,
|
||||||
#[serde(default = "Vec::new")]
|
#[serde(default = "Vec::new")]
|
||||||
pub permissions: Vec<PermRule>,
|
pub permissions: Vec<PermRule>,
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,6 @@ pub use resources::{
|
|||||||
ResourceDB,
|
ResourceDB,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod access;
|
|
||||||
|
|
||||||
use lmdb::Error;
|
use lmdb::Error;
|
||||||
use rkyv::ser::serializers::AlignedSerializer;
|
use rkyv::ser::serializers::AlignedSerializer;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ pub mod oid;
|
|||||||
mod varint;
|
mod varint;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
mod permissions;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
@ -1,97 +1,10 @@
|
|||||||
//! Access control logic
|
//! Access control logic
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use slog::Logger;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::convert::{TryFrom, Into};
|
use std::convert::{TryFrom, Into};
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
use crate::error::Result;
|
|
||||||
|
|
||||||
use crate::config::Config;
|
|
||||||
|
|
||||||
pub struct AccessControl {
|
|
||||||
internal: HashMap<RoleIdentifier, Role>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(_log: Logger, config: &Config, _env: Arc<lmdb::Environment>)
|
|
||||||
-> std::result::Result<HashMap<RoleIdentifier, Role>, crate::error::Error>
|
|
||||||
{
|
|
||||||
Ok(config.roles.iter().map(|(name, cfg)| {
|
|
||||||
let id = RoleIdentifier::new(name, "internal");
|
|
||||||
let parents = cfg.parents.iter().map(|n| RoleIdentifier::new(n, "internal")).collect();
|
|
||||||
let role = Role::new(parents, cfg.permissions.clone());
|
|
||||||
(id, role)
|
|
||||||
}).collect())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const ADMINPERM: &'static str = "bffh.admin";
|
|
||||||
pub fn admin_perm() -> &'static Permission {
|
|
||||||
Permission::new(ADMINPERM)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AccessControl {
|
|
||||||
pub fn new(internal: HashMap<RoleIdentifier, Role>) -> Self {
|
|
||||||
Self {
|
|
||||||
internal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dump_roles(&self) -> Result<Vec<(RoleIdentifier, Role)>> {
|
|
||||||
Ok(self.internal.iter().map(|(k,v)| (k.clone(), v.clone())).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for AccessControl {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let mut b = f.debug_struct("AccessControl");
|
|
||||||
b.field("internal", &self.internal.get_type_name().to_string());
|
|
||||||
b.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait RoleDB {
|
|
||||||
fn get_type_name(&self) -> &'static str;
|
|
||||||
|
|
||||||
fn get_role(&self, role_id: &RoleIdentifier) -> Result<Option<Role>>;
|
|
||||||
|
|
||||||
/// Tally a role dependency tree into a set
|
|
||||||
///
|
|
||||||
/// A Default implementation exists which adapter may overwrite with more efficient
|
|
||||||
/// implementations.
|
|
||||||
fn tally_role(&self, roles: &mut HashMap<RoleIdentifier, Role>, role_id: &RoleIdentifier) -> Result<()> {
|
|
||||||
if let Some(role) = self.get_role(role_id)? {
|
|
||||||
// Only check and tally parents of a role at the role itself if it's the first time we
|
|
||||||
// see it
|
|
||||||
if !roles.contains_key(&role_id) {
|
|
||||||
for parent in role.parents.iter() {
|
|
||||||
self.tally_role(roles, parent)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
roles.insert(role_id.clone(), role);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RoleDB for HashMap<RoleIdentifier, Role> {
|
|
||||||
fn get_type_name(&self) -> &'static str {
|
|
||||||
"Internal"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_role(&self, role_id: &RoleIdentifier) -> Result<Option<Role>> {
|
|
||||||
Ok(self.get(role_id).cloned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A "Role" from the Authorization perspective
|
/// A "Role" from the Authorization perspective
|
||||||
///
|
///
|
||||||
/// You can think of a role as a bundle of permissions relating to other roles. In most cases a
|
/// You can think of a role as a bundle of permissions relating to other roles. In most cases a
|
||||||
@ -104,7 +17,7 @@ impl RoleDB for HashMap<RoleIdentifier, Role> {
|
|||||||
/// of a machine; if later on a similar enough machine is put to use the administrator can just add
|
/// of a machine; if later on a similar enough machine is put to use the administrator can just add
|
||||||
/// the permission for that machine to an already existing role instead of manually having to
|
/// the permission for that machine to an already existing role instead of manually having to
|
||||||
/// assign to all users.
|
/// assign to all users.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct Role {
|
pub struct Role {
|
||||||
// If a role doesn't define parents, default to an empty Vec.
|
// If a role doesn't define parents, default to an empty Vec.
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
@ -159,7 +72,7 @@ fn split_once(s: &str, split: char) -> Option<(&str, &str)> {
|
|||||||
.map(|idx| (&s[..idx], &s[(idx+1)..]))
|
.map(|idx| (&s[..idx], &s[(idx+1)..]))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(try_from = "String")]
|
#[serde(try_from = "String")]
|
||||||
#[serde(into = "String")]
|
#[serde(into = "String")]
|
||||||
/// Universal (relative) id of a role
|
/// Universal (relative) id of a role
|
||||||
@ -232,30 +145,11 @@ impl fmt::Display for RoleFromStrError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
||||||
/// An identifier for a permission
|
|
||||||
// XXX: Does remote permissions ever make sense?
|
|
||||||
// I mean we kinda get them for free so maybe?
|
|
||||||
pub enum PermIdentifier {
|
|
||||||
Local(PermRule),
|
|
||||||
Remote(PermRule, String),
|
|
||||||
}
|
|
||||||
impl fmt::Display for PermIdentifier {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
PermIdentifier::Local(perm)
|
|
||||||
=> write!(f, "{}", perm),
|
|
||||||
PermIdentifier::Remote(perm, source)
|
|
||||||
=> write!(f, "{}@{}", perm, source),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_sep_char(c: char) -> bool {
|
fn is_sep_char(c: char) -> bool {
|
||||||
c == '.'
|
c == '.'
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
/// A set of privileges to a thing
|
/// A set of privileges to a thing
|
||||||
pub struct PrivilegesBuf {
|
pub struct PrivilegesBuf {
|
||||||
/// Which permission is required to know about the existance of this thing
|
/// Which permission is required to know about the existance of this thing
|
||||||
@ -268,7 +162,7 @@ pub struct PrivilegesBuf {
|
|||||||
pub manage: PermissionBuf
|
pub manage: PermissionBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
/// An owned permission string
|
/// An owned permission string
|
||||||
@ -309,7 +203,7 @@ impl PermissionBuf {
|
|||||||
self.inner.push_str(perm.as_str())
|
self.inner.push_str(perm.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn from_string(inner: String) -> Self {
|
pub const fn from_string_unchecked(inner: String) -> Self {
|
||||||
Self { inner }
|
Self { inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,11 +214,21 @@ impl PermissionBuf {
|
|||||||
pub fn into_string(self) -> String {
|
pub fn into_string(self) -> String {
|
||||||
self.inner
|
self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.inner.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsRef<String> for PermissionBuf {
|
||||||
|
#[inline(always)]
|
||||||
|
fn as_ref(&self) -> &String {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl AsRef<str> for PermissionBuf {
|
impl AsRef<str> for PermissionBuf {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &str {
|
fn as_ref(&self) -> &str {
|
||||||
&self.inner[..]
|
&self.inner.as_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl AsRef<Permission> for PermissionBuf {
|
impl AsRef<Permission> for PermissionBuf {
|
||||||
@ -345,19 +249,21 @@ impl fmt::Display for PermissionBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(PartialEq, Eq, Hash, Debug)]
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||||
|
#[repr(transparent)]
|
||||||
/// A borrowed permission string
|
/// A borrowed permission string
|
||||||
///
|
///
|
||||||
/// Permissions have total equality and partial ordering.
|
/// Permissions have total equality and partial ordering.
|
||||||
/// Specifically permissions on the same path in a tree can be compared for specificity.
|
/// Specifically permissions on the same path in a tree can be compared for specificity.
|
||||||
/// This means that ```(bffh.perm) > (bffh.perm.sub) == true```
|
/// This means that ```(bffh.perm) > (bffh.perm.sub) == true```
|
||||||
/// but ```(bffh.perm) > (unrelated.but.specific.perm) == false```
|
/// but ```(bffh.perm) > (unrelated.but.more.specific.perm) == false```.
|
||||||
|
/// This allows to check if PermRule a grants Perm b by checking `a > b`.
|
||||||
pub struct Permission {
|
pub struct Permission {
|
||||||
inner: str
|
inner: str
|
||||||
}
|
}
|
||||||
impl Permission {
|
impl Permission {
|
||||||
pub fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Permission {
|
pub fn new<S: AsRef<str> + ?Sized>(s: &S) -> &Permission {
|
||||||
|
// Safe because s is a valid reference
|
||||||
unsafe { &*(s.as_ref() as *const str as *const Permission) }
|
unsafe { &*(s.as_ref() as *const str as *const Permission) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,7 +296,8 @@ impl PartialOrd for Permission {
|
|||||||
(None, None) => Some(Ordering::Equal),
|
(None, None) => Some(Ordering::Equal),
|
||||||
(Some(_), None) => Some(Ordering::Less),
|
(Some(_), None) => Some(Ordering::Less),
|
||||||
(None, Some(_)) => Some(Ordering::Greater),
|
(None, Some(_)) => Some(Ordering::Greater),
|
||||||
(Some(_), Some(_)) => panic!("Broken contract in Permission::partial_cmp: sides should never be both Some!"),
|
(Some(_), Some(_)) => unreachable!("Broken contract in Permission::partial_cmp: sides \
|
||||||
|
should never be both Some!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -402,7 +309,7 @@ impl AsRef<Permission> for Permission {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(try_from = "String")]
|
#[serde(try_from = "String")]
|
||||||
#[serde(into = "String")]
|
#[serde(into = "String")]
|
||||||
pub enum PermRule {
|
pub enum PermRule {
|
||||||
@ -420,8 +327,8 @@ pub enum PermRule {
|
|||||||
/// i.e. `Children("bffh.perm")` grants bffh.perm.sub, bffh.perm.sub.two and also bffh.perm
|
/// i.e. `Children("bffh.perm")` grants bffh.perm.sub, bffh.perm.sub.two and also bffh.perm
|
||||||
/// itself.
|
/// itself.
|
||||||
Subtree(PermissionBuf),
|
Subtree(PermissionBuf),
|
||||||
// This lacks what LDAP calls ONELEVEL: The ability to grant the exact children but not several
|
// This lacks what LDAP calls "ONELEVEL": The ability to grant the exact children but not several
|
||||||
// levels deep, i.e. Onelevel("bffh.perm") grants bffh.perm.sub *BUT NOT* bffh.perm.sub.two or
|
// levels deep, i.e. `Onelevel("bffh.perm")` grants bffh.perm.sub *BUT NOT* bffh.perm.sub.two or
|
||||||
// bffh.perm itself.
|
// bffh.perm itself.
|
||||||
// I can't think of a reason to use that so I'm skipping it for now.
|
// I can't think of a reason to use that so I'm skipping it for now.
|
||||||
}
|
}
|
||||||
@ -478,13 +385,13 @@ impl TryFrom<String> for PermRule {
|
|||||||
match &input[len-2..len] {
|
match &input[len-2..len] {
|
||||||
".+" => {
|
".+" => {
|
||||||
input.truncate(len-2);
|
input.truncate(len-2);
|
||||||
Ok(PermRule::Children(PermissionBuf::from_string(input)))
|
Ok(PermRule::Children(PermissionBuf::from_string_unchecked(input)))
|
||||||
},
|
},
|
||||||
".*" => {
|
".*" => {
|
||||||
input.truncate(len-2);
|
input.truncate(len-2);
|
||||||
Ok(PermRule::Subtree(PermissionBuf::from_string(input)))
|
Ok(PermRule::Subtree(PermissionBuf::from_string_unchecked(input)))
|
||||||
},
|
},
|
||||||
_ => Ok(PermRule::Base(PermissionBuf::from_string(input))),
|
_ => Ok(PermRule::Base(PermissionBuf::from_string_unchecked(input))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -496,25 +403,43 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn permission_ord_test() {
|
fn permission_ord_test() {
|
||||||
assert!(PermissionBuf::from_string("bffh.perm".to_string())
|
assert!(PermissionBuf::from_string_unchecked("bffh.perm".to_string())
|
||||||
> PermissionBuf::from_string("bffh.perm.sub".to_string()));
|
> PermissionBuf::from_string_unchecked("bffh.perm.sub".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn permission_simple_check_test() {
|
fn permission_simple_check_test() {
|
||||||
let perm = PermissionBuf::from_string("test.perm".to_string());
|
let perm = PermissionBuf::from_string_unchecked("test.perm".to_string());
|
||||||
let rule = PermRule::Base(perm.clone());
|
let rule = PermRule::Base(perm.clone());
|
||||||
|
|
||||||
assert!(rule.match_perm(&perm));
|
assert!(rule.match_perm(&perm));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn permission_children_checks_only_children() {
|
fn permission_children_checks_only_children() {
|
||||||
let perm = PermissionBuf::from_string("test.perm".to_string());
|
let perm = PermissionBuf::from_string_unchecked("test.perm".to_string());
|
||||||
let rule = PermRule::Children(perm.clone());
|
let rule = PermRule::Children(perm.clone());
|
||||||
|
|
||||||
|
assert_eq!(rule.match_perm(&perm), false);
|
||||||
|
|
||||||
|
let perm2 = PermissionBuf::from_string_unchecked("test.perm.child".to_string());
|
||||||
|
let perm3 = PermissionBuf::from_string_unchecked("test.perm.child.deeper".to_string());
|
||||||
|
assert!(rule.match_perm(&perm2));
|
||||||
|
assert!(rule.match_perm(&perm3));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn permission_subtree_checks_base() {
|
||||||
|
let perm = PermissionBuf::from_string_unchecked("test.perm".to_string());
|
||||||
|
let rule = PermRule::Subtree(perm.clone());
|
||||||
|
|
||||||
assert!(rule.match_perm(&perm));
|
assert!(rule.match_perm(&perm));
|
||||||
|
|
||||||
|
let perm2 = PermissionBuf::from_string_unchecked("test.perm.child".to_string());
|
||||||
|
let perm3 = PermissionBuf::from_string_unchecked("test.perm.child.deeper".to_string());
|
||||||
|
|
||||||
|
assert!(rule.match_perm(&perm2));
|
||||||
|
assert!(rule.match_perm(&perm3));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -542,63 +467,19 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn load_examples_roles_test() {
|
|
||||||
let mut roles = Role::load_file("examples/roles.toml")
|
|
||||||
.expect("Couldn't load the example role defs. Does `examples/roles.toml` exist?");
|
|
||||||
|
|
||||||
let expected = vec![
|
|
||||||
(RoleIdentifier { name: "anotherrole".to_string(), source: "lmdb".to_string() },
|
|
||||||
Role {
|
|
||||||
parents: vec![],
|
|
||||||
permissions: vec![],
|
|
||||||
}),
|
|
||||||
(RoleIdentifier { name: "testrole".to_string(), source: "lmdb".to_string() },
|
|
||||||
Role {
|
|
||||||
parents: vec![],
|
|
||||||
permissions: vec![
|
|
||||||
PermRule::Subtree(PermissionBuf::from_string("lab.test".to_string()))
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
(RoleIdentifier { name: "somerole".to_string(), source: "lmdb".to_string() },
|
|
||||||
Role {
|
|
||||||
parents: vec![
|
|
||||||
RoleIdentifier::local_from_str("lmdb".to_string(), "testparent".to_string()),
|
|
||||||
],
|
|
||||||
permissions: vec![
|
|
||||||
PermRule::Base(PermissionBuf::from_string("lab.some.admin".to_string()))
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
(RoleIdentifier { name: "testparent".to_string(), source: "lmdb".to_string() },
|
|
||||||
Role {
|
|
||||||
parents: vec![],
|
|
||||||
permissions: vec![
|
|
||||||
PermRule::Base(PermissionBuf::from_string("lab.some.write".to_string())),
|
|
||||||
PermRule::Base(PermissionBuf::from_string("lab.some.read".to_string())),
|
|
||||||
PermRule::Base(PermissionBuf::from_string("lab.some.disclose".to_string())),
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (id, role) in expected {
|
|
||||||
assert_eq!(roles.remove(&id).unwrap(), role);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(roles.is_empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rules_from_string_test() {
|
fn rules_from_string_test() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PermRule::Base(PermissionBuf::from_string("bffh.perm".to_string())),
|
PermRule::Base(PermissionBuf::from_string_unchecked("bffh.perm".to_string())),
|
||||||
PermRule::try_from("bffh.perm".to_string()).unwrap()
|
PermRule::try_from("bffh.perm".to_string()).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PermRule::Children(PermissionBuf::from_string("bffh.perm".to_string())),
|
PermRule::Children(PermissionBuf::from_string_unchecked("bffh.perm".to_string())),
|
||||||
PermRule::try_from("bffh.perm.+".to_string()).unwrap()
|
PermRule::try_from("bffh.perm.+".to_string()).unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PermRule::Subtree(PermissionBuf::from_string("bffh.perm".to_string())),
|
PermRule::Subtree(PermissionBuf::from_string_unchecked("bffh.perm".to_string())),
|
||||||
PermRule::try_from("bffh.perm.*".to_string()).unwrap()
|
PermRule::try_from("bffh.perm.*".to_string()).unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user