#[cfg(feature = "server")]
mod server_impl {
pub use super::application_validator::ApplicationValidator;
pub use super::collision_checker::PermissionCollisionChecker;
pub use super::errors::PermissionsError;
pub use super::permission_collision::PermissionCollision;
pub use super::validation_report::ValidationReport;
}
#[cfg(feature = "server")]
pub use server_impl::*;
pub use self::as_permission_name::AsPermissionName;
pub use self::permission_id::PermissionId;
use roaring::RoaringTreemap;
use serde::{Deserialize, Serialize};
use std::fmt;
#[cfg(feature = "server")]
mod application_validator;
mod as_permission_name;
#[cfg(feature = "server")]
mod collision_checker;
#[cfg(feature = "server")]
pub mod errors;
#[cfg(feature = "server")]
pub mod mapping;
#[cfg(feature = "server")]
mod permission_collision;
mod permission_id;
#[cfg(feature = "server")]
pub mod validate_permissions;
#[cfg(feature = "server")]
mod validation_report;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Permissions {
bitmap: RoaringTreemap,
}
impl Permissions {
pub fn new() -> Self {
Self {
bitmap: RoaringTreemap::new(),
}
}
pub fn grant<P>(&mut self, permission: P) -> &mut Self
where
P: Into<PermissionId>,
{
let permission_id = permission.into();
self.bitmap.insert(permission_id.as_u64());
self
}
pub fn revoke<P>(&mut self, permission: P) -> &mut Self
where
P: Into<PermissionId>,
{
let permission_id = permission.into();
self.bitmap.remove(permission_id.as_u64());
self
}
pub fn has<P>(&self, permission: P) -> bool
where
P: Into<PermissionId>,
{
let permission_id = permission.into();
self.bitmap.contains(permission_id.as_u64())
}
pub fn has_all<I, P>(&self, permissions: I) -> bool
where
I: IntoIterator<Item = P>,
P: Into<PermissionId>,
{
permissions.into_iter().all(|p| self.has(p))
}
pub fn has_any<I, P>(&self, permissions: I) -> bool
where
I: IntoIterator<Item = P>,
P: Into<PermissionId>,
{
permissions.into_iter().any(|p| self.has(p))
}
pub fn len(&self) -> usize {
self.bitmap.len() as usize
}
pub fn is_empty(&self) -> bool {
self.bitmap.is_empty()
}
pub fn clear(&mut self) {
self.bitmap.clear();
}
pub fn union(&mut self, other: &Permissions) -> &mut Self {
self.bitmap |= &other.bitmap;
self
}
pub fn intersection(&mut self, other: &Permissions) -> &mut Self {
self.bitmap &= &other.bitmap;
self
}
pub fn difference(&mut self, other: &Permissions) -> &mut Self {
self.bitmap -= &other.bitmap;
self
}
pub fn with<P>(mut self, permission: P) -> Self
where
P: Into<PermissionId>,
{
self.grant(permission);
self
}
pub fn build(self) -> Self {
self
}
pub fn iter(&self) -> impl Iterator<Item = u64> + '_ {
self.bitmap.iter()
}
#[cfg(feature = "server")]
pub(crate) fn bitmap_mut(&mut self) -> &mut roaring::RoaringTreemap {
&mut self.bitmap
}
}
impl Default for Permissions {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for Permissions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Permissions({})", self.len())
}
}
impl From<roaring::RoaringTreemap> for Permissions {
fn from(bitmap: roaring::RoaringTreemap) -> Self {
Self { bitmap }
}
}
impl From<Permissions> for roaring::RoaringTreemap {
fn from(permissions: Permissions) -> Self {
permissions.bitmap
}
}
impl AsRef<roaring::RoaringTreemap> for Permissions {
fn as_ref(&self) -> &roaring::RoaringTreemap {
&self.bitmap
}
}
impl<P> std::iter::FromIterator<P> for Permissions
where
P: Into<PermissionId>,
{
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
let mut perms = Self::new();
for permission in iter {
perms.grant(permission);
}
perms
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_permissions_is_empty() {
let permissions = Permissions::new();
assert!(permissions.is_empty());
assert_eq!(permissions.len(), 0);
}
#[test]
fn grant_and_has_permission() {
let mut permissions = Permissions::new();
permissions.grant("read:profile");
assert!(permissions.has("read:profile"));
assert!(!permissions.has("write:profile"));
assert_eq!(permissions.len(), 1);
assert!(!permissions.is_empty());
}
#[test]
fn grant_chaining() {
let mut permissions = Permissions::new();
permissions
.grant("read:profile")
.grant("write:profile")
.grant("delete:profile");
assert!(permissions.has("read:profile"));
assert!(permissions.has("write:profile"));
assert!(permissions.has("delete:profile"));
assert_eq!(permissions.len(), 3);
}
#[test]
fn revoke_permission() {
let mut permissions: Permissions = ["read:profile", "write:profile"].into_iter().collect();
permissions.revoke("write:profile");
assert!(permissions.has("read:profile"));
assert!(!permissions.has("write:profile"));
assert_eq!(permissions.len(), 1);
}
#[test]
fn has_all_permissions() {
let permissions: Permissions = ["read:profile", "write:profile", "read:posts"]
.into_iter()
.collect();
assert!(permissions.has_all(["read:profile", "write:profile"]));
assert!(permissions.has_all(["read:profile"]));
assert!(!permissions.has_all(["read:profile", "admin:users"]));
assert!(permissions.has_all(Vec::<&str>::new())); }
#[test]
fn has_any_permission() {
let permissions: Permissions = ["read:profile"].into_iter().collect();
assert!(permissions.has_any(["read:profile", "write:profile"]));
assert!(permissions.has_any(["write:profile", "read:profile"]));
assert!(!permissions.has_any(["write:profile", "admin:users"]));
assert!(!permissions.has_any(Vec::<&str>::new())); }
#[test]
fn clear_permissions() {
let mut permissions: Permissions = ["read:profile", "write:profile"].into_iter().collect();
assert!(!permissions.is_empty());
permissions.clear();
assert!(permissions.is_empty());
assert_eq!(permissions.len(), 0);
}
#[test]
fn union_permissions() {
let mut permissions1: Permissions = ["read:profile"].into_iter().collect();
let permissions2: Permissions = ["write:profile", "read:posts"].into_iter().collect();
permissions1.union(&permissions2);
assert!(permissions1.has("read:profile"));
assert!(permissions1.has("write:profile"));
assert!(permissions1.has("read:posts"));
assert_eq!(permissions1.len(), 3);
}
#[test]
fn intersection_permissions() {
let mut permissions1: Permissions = ["read:profile", "write:profile"].into_iter().collect();
let permissions2: Permissions = ["read:profile", "admin:users"].into_iter().collect();
permissions1.intersection(&permissions2);
assert!(permissions1.has("read:profile"));
assert!(!permissions1.has("write:profile"));
assert!(!permissions1.has("admin:users"));
assert_eq!(permissions1.len(), 1);
}
#[test]
fn difference_permissions() {
let mut permissions1 = Permissions::from_iter(["read:profile", "write:profile"]);
let permissions2 = Permissions::from_iter(["write:profile"]);
permissions1.difference(&permissions2);
assert!(permissions1.has("read:profile"));
assert!(!permissions1.has("write:profile"));
assert_eq!(permissions1.len(), 1);
}
#[test]
fn builder_pattern() {
let permissions = Permissions::new()
.with("read:profile")
.with("write:profile")
.build();
assert!(permissions.has("read:profile"));
assert!(permissions.has("write:profile"));
assert_eq!(permissions.len(), 2);
}
#[test]
fn from_iter() {
let permissions: Permissions = ["read:profile", "write:profile", "read:posts"]
.into_iter()
.collect();
assert!(permissions.has("read:profile"));
assert!(permissions.has("write:profile"));
assert!(permissions.has("read:posts"));
assert_eq!(permissions.len(), 3);
}
#[test]
fn permissions_are_deterministic() {
let permissions1 = Permissions::from_iter(["read:profile"]);
let permissions2 = Permissions::from_iter(["read:profile"]);
assert_eq!(permissions1, permissions2);
assert!(permissions1.has("read:profile"));
assert!(permissions2.has("read:profile"));
}
#[test]
fn display_implementation() {
let permissions = Permissions::from_iter(["read:profile", "write:profile"]);
let display = format!("{}", permissions);
assert_eq!(display, "Permissions(2)");
}
}