use std::fmt;
use super::policy::Policy;
use super::resource::Resource;
use crate::authentication::enums::subject_type::SubjectType;
use crate::authentication::model::subject::Subject;
use crate::authorization::enums::policy_type::PolicyType;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Acl {
subject_key: String,
subject_type: SubjectType,
policies: Vec<Policy>,
}
impl Acl {
pub fn of_subject_and_policy<S: Subject>(subject: &S, policy: Policy) -> Self {
Self::of_subject_and_policies(subject, vec![policy])
}
pub fn of_subject_and_policies<S: Subject>(subject: &S, policies: Vec<Policy>) -> Self {
Acl {
subject_key: subject.subject_key().to_string(),
subject_type: subject.subject_type(),
policies,
}
}
pub fn of(subject_key: impl Into<String>, subject_type: SubjectType, policy: Policy) -> Self {
Self::of_with_policies(subject_key, subject_type, vec![policy])
}
pub fn of_with_policies(subject_key: impl Into<String>, subject_type: SubjectType, policies: Vec<Policy>) -> Self {
Acl {
subject_key: normalize_subject_key(subject_key.into(), subject_type),
subject_type,
policies,
}
}
pub fn update_policy(&mut self, policy: Policy) {
self.update_policies(vec![policy])
}
pub fn update_policies(&mut self, policies: Vec<Policy>) {
if self.policies.is_empty() {
self.policies = Vec::new();
}
for new_policy in policies {
match self.get_policy_mut(new_policy.policy_type()) {
None => self.policies.push(new_policy),
Some(old_policy) => old_policy.update_entry(new_policy.entries().clone()),
}
}
}
pub fn delete_policy(&mut self, policy_type: PolicyType, resource: &Resource) {
if let Some(policy) = self.get_policy_mut(policy_type) {
policy.delete_entry(resource);
if policy.entries().is_empty() {
if let Some(pos) = self.policies.iter().position(|p| p.policy_type() == policy_type) {
self.policies.remove(pos);
}
}
}
}
pub fn get_policy(&self, policy_type: PolicyType) -> Option<&Policy> {
self.policies.iter().find(|p| p.policy_type() == policy_type)
}
fn get_policy_mut(&mut self, policy_type: PolicyType) -> Option<&mut Policy> {
self.policies.iter_mut().find(|p| p.policy_type() == policy_type)
}
pub fn subject_key(&self) -> &str {
&self.subject_key
}
pub fn subject_type(&self) -> SubjectType {
self.subject_type
}
pub fn policies(&self) -> &Vec<Policy> {
&self.policies
}
pub fn set_policies(&mut self, p: Vec<Policy>) {
self.policies = p;
}
pub fn set_subject_key(&mut self, k: impl Into<String>) {
self.subject_key = k.into();
}
pub fn set_subject_type(&mut self, t: SubjectType) {
self.subject_type = t;
}
}
fn normalize_subject_key(subject_key: String, subject_type: SubjectType) -> String {
if let Some((_, raw_name)) = subject_key.split_once(':') {
return format!("{}:{}", subject_type.name(), raw_name);
}
format!("{}:{}", subject_type.name(), subject_key)
}
impl fmt::Display for Acl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Acl{{subject_key={}, subject_type={}, policies_len={}}}",
self.subject_key,
self.subject_type.name(),
self.policies.len()
)
}
}
#[cfg(test)]
mod tests {
use rocketmq_security_api::Action;
use rocketmq_security_api::ResourcePattern;
use rocketmq_security_api::ResourceType;
use super::*;
use crate::authentication::model::user::User;
use crate::authorization::enums::decision::Decision;
use crate::authorization::model::policy::Policy;
#[test]
fn test_of_and_update_delete() {
let user = User::of("alice");
let r1 = Resource::of(ResourceType::Topic, Some("t1".to_string()), ResourcePattern::Literal);
let r2 = Resource::of(ResourceType::Topic, Some("t2".to_string()), ResourcePattern::Literal);
let mut acl = Acl::of_subject_and_policies(
&user,
vec![Policy::of(vec![r1.clone()], vec![Action::Pub], None, Decision::Allow)],
);
assert_eq!(acl.policies().len(), 1);
acl.update_policy(Policy::of(vec![r2.clone()], vec![Action::Pub], None, Decision::Allow));
assert_eq!(acl.policies().len(), 1);
assert_eq!(acl.policies().first().unwrap().entries().len(), 2);
acl.delete_policy(PolicyType::Custom, &r1);
assert_eq!(acl.policies().len(), 1);
assert_eq!(acl.policies().first().unwrap().entries().len(), 1);
}
}