use std::ops::Deref;
use std::str::FromStr as _;
use anyhow::Result;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use super::{Level, Resource, ResourceKind};
use crate::iam::{AuthLimit, Role};
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Actor {
res: Resource,
roles: Vec<Role>,
}
impl Default for Actor {
fn default() -> Self {
Self {
res: ResourceKind::Actor.on_level(Level::No),
roles: Vec::new(),
}
}
}
impl std::fmt::Display for Actor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.res.level() == &Level::No {
return write!(f, "Actor::Anonymous");
}
write!(
f,
"{}{}::{}({})",
self.res.level(),
self.res.kind(),
self.res.id(),
self.roles.iter().map(|r| format!("{}", r)).collect::<Vec<String>>().join(", ")
)
}
}
impl Actor {
pub(crate) fn new(id: String, roles: Vec<Role>, level: Level) -> Self {
Self {
res: Resource::new(id, super::ResourceKind::Actor, level),
roles,
}
}
pub fn from_role_names(id: String, roles: &[String], level: Level) -> Result<Self> {
let roles = roles.iter().map(|x| Role::from_str(x)).collect::<Result<Vec<_>, _>>()?;
Ok(Self::new(id, roles, level))
}
pub(crate) fn has_role(&self, role: Role) -> bool {
self.roles.contains(&role)
}
pub(crate) fn has_owner_role(&self) -> bool {
self.roles.iter().any(|r| r.eq(&Role::Owner))
}
pub(crate) fn has_editor_role(&self) -> bool {
self.roles.iter().any(|r| r.eq(&Role::Owner) || r.eq(&Role::Editor))
}
pub(crate) fn has_viewer_role(&self) -> bool {
self.roles.iter().any(|r| r.eq(&Role::Owner) || r.eq(&Role::Editor) || r.eq(&Role::Viewer))
}
pub(crate) fn new_limited(&self, limit: &AuthLimit) -> Self {
if self.res.level().is_record() || self.res.level().is_anonymous() {
return self.clone();
}
let level = if limit.level.sublevel_of(self.res.level()) {
limit.level.clone()
} else {
self.res.level().clone()
};
let mut roles = self.roles.clone();
if let Some(role) = limit.role.as_ref() {
roles.retain(|r| r <= role);
if roles.is_empty() {
roles.push(*role);
}
}
if roles.is_empty() {
roles.push(Role::Viewer);
}
Self::new(self.res.id().to_string(), roles, level)
}
pub(crate) fn max_role(&self) -> Option<Role> {
if self.roles.is_empty() {
return None;
}
Some(self.roles.iter().fold(Role::Viewer, |max_role, &role| {
if role > max_role {
role
} else {
max_role
}
}))
}
}
impl Deref for Actor {
type Target = Resource;
fn deref(&self) -> &Self::Target {
&self.res
}
}