use crate::errors::{ErrorKind, MinosError};
use crate::group::GroupId;
use crate::resources::{Owner, ResourceType};
use crate::user::UserAttributes;
use crate::Status;
use chrono::{Duration, NaiveDateTime, Utc};
#[derive(Debug, PartialEq, Clone, PartialOrd)]
pub enum Permission {
Create,
Read,
Update,
Delete,
#[cfg(feature = "custom_permission")]
Custom(String),
}
#[cfg(not(feature = "custom_permission"))]
impl From<u8> for Permission {
fn from(n: u8) -> Self {
match n {
3 => Permission::Delete,
2 => Permission::Update,
1 => Permission::Create,
_ => Permission::Read,
}
}
}
impl ToString for Permission {
fn to_string(&self) -> String {
format!("{:?}", self).to_lowercase()
}
}
#[cfg(not(feature = "custom_permission"))]
impl From<&str> for Permission {
fn from(str: &str) -> Self {
if str == Permission::Create.to_string() {
Permission::Create
} else if str == Permission::Update.to_string() {
Permission::Update
} else if str == Permission::Delete.to_string() {
Permission::Delete
} else {
Permission::Read
}
}
}
#[cfg(feature = "custom_permission")]
impl From<&str> for Permission {
fn from(str: &str) -> Self {
if str == Permission::Create.to_string() {
Permission::Create
} else if str == Permission::Update.to_string() {
Permission::Update
} else if str == Permission::Delete.to_string() {
Permission::Delete
} else if str == Permission::Read.to_string() {
Permission::Read
} else {
Permission::Custom(str.to_string())
}
}
}
impl Permission {
#[cfg(not(feature = "custom_permission"))]
pub fn as_u8(&self) -> u8 {
self.clone() as u8
}
pub fn required_msg(&self) -> String {
format!("{:?} permission is required.", self)
}
pub fn crud() -> Vec<Permission> {
vec![
Permission::Create,
Permission::Read,
Permission::Update,
Permission::Delete,
]
}
pub fn rud() -> Vec<Permission> {
vec![Permission::Read, Permission::Update, Permission::Delete]
}
}
#[derive(Debug, PartialEq, Clone, PartialOrd)]
pub struct Authorization {
pub(crate) permissions: Vec<Permission>,
pub(crate) user_id: String,
pub(crate) resource_id: String,
pub(crate) resource_type: String,
pub(crate) expiration: NaiveDateTime,
}
impl Authorization {
pub fn permissions(&self) -> &Vec<Permission> {
&self.permissions
}
pub fn user_id(&self) -> &str {
&self.user_id
}
pub fn resource_id(&self) -> &str {
&self.resource_id
}
pub fn resource_type(&self) -> &str {
&self.resource_type
}
pub fn expiration(&self) -> NaiveDateTime {
self.expiration
}
fn basic_check(&self, resource_id: &str, user: &UserAttributes) -> Result<(), MinosError> {
if &self.resource_id != resource_id {
return Err(MinosError::new(
ErrorKind::Authorization,
"Authorization created for another resource",
));
}
if self.expiration <= Utc::now().naive_utc() {
return Err(MinosError::new(
ErrorKind::Authorization,
"The Authorization is expired",
));
}
if &user.id != &self.user_id {
return Err(MinosError::new(
ErrorKind::Authorization,
&format!("This Authorization is not for the user {}", &user.id),
));
}
Ok(())
}
pub fn check(
&self,
resource_id: &str,
user: &UserAttributes,
required_permission: &Permission,
) -> Result<(), MinosError> {
let _ = self.basic_check(resource_id, user)?;
if !&self.permissions.contains(&required_permission) {
return Err(MinosError::new(
ErrorKind::Authorization,
&required_permission.required_msg(),
));
}
Ok(())
}
pub fn multi_permissions_check(
&self,
resource_id: &str,
user: &UserAttributes,
required_permissions: &Vec<Permission>,
) -> Result<(), MinosError> {
let _ = self.basic_check(resource_id, user)?;
for permission in required_permissions {
if !&self.permissions.contains(permission) {
return Err(MinosError::new(
ErrorKind::Authorization,
&permission.required_msg(),
));
}
}
Ok(())
}
}
#[derive(PartialEq, Debug, Clone, PartialOrd, Default)]
pub struct Policy {
pub(crate) duration: u16,
pub(crate) by_owner: bool,
pub(crate) groups_ids: Option<Vec<GroupId>>,
pub(crate) permissions: Vec<Permission>,
}
impl Policy {
pub fn new(
duration: u16,
by_owner: bool,
groups_ids: Option<Vec<GroupId>>,
permissions: Vec<Permission>,
) -> Self {
Self {
duration,
by_owner,
groups_ids,
permissions,
}
}
pub fn duration(&self) -> u16 {
self.duration
}
pub fn by_owner(&self) -> bool {
self.by_owner
}
pub fn groups_ids(&self) -> &Option<Vec<GroupId>> {
&self.groups_ids
}
pub fn permissions(&self) -> &Vec<Permission> {
&self.permissions
}
}
pub struct AuthorizationBuilder<'b> {
resource_type: &'b ResourceType,
}
impl<'b> AuthorizationBuilder<'b> {
pub fn new(resource_type: &'b ResourceType) -> Self {
Self { resource_type }
}
fn check_groups(&self, user: &UserAttributes, policy: &Policy) -> Result<(), MinosError> {
if let Some(possible_ids) = &policy.groups_ids {
for id in possible_ids {
if user.groups.contains(&id) {
return Ok(());
}
}
return Err(MinosError::new(
ErrorKind::Authorization,
"The user is not in the correct group",
));
}
Ok(())
}
fn same_group_check(group_id: GroupId, user: &UserAttributes) -> Result<(), MinosError> {
if !&user.groups.contains(&group_id) {
return Err(MinosError::new(
ErrorKind::Authorization,
"The user is not in the owning group",
));
}
Ok(())
}
fn same_user_check(user_id: &str, user: &UserAttributes) -> Result<(), MinosError> {
if user_id != &user.id {
return Err(MinosError::new(
ErrorKind::Authorization,
"The user is not the owner",
));
}
Ok(())
}
fn by_owner_check(&self, user: &UserAttributes) -> Result<(), MinosError> {
match &self.resource_type.owner {
None => {
return Err(MinosError::new(
ErrorKind::IncompatibleAuthPolicy,
"The resource haven't an owner",
));
}
Some(owner) => match owner {
Owner::User(id) => Self::same_user_check(id, &user)?,
Owner::Group(id) => Self::same_group_check(GroupId::from(id.as_str()), &user)?,
},
}
Ok(())
}
pub fn build_by_policy(
&self,
policy: &Policy,
resource_id: &str,
user: &UserAttributes,
) -> Result<Authorization, MinosError> {
if user.status != Status::Active {
return Err(MinosError::new(
ErrorKind::InactiveUser,
"The user is not active",
));
}
if !&self.resource_type.policies.contains(&policy) {
return Err(MinosError::new(
ErrorKind::IncompatibleAuthPolicy,
"The policy not corresponds to resource type",
));
}
if policy.by_owner {
let _ = self.by_owner_check(&user)?;
} else {
let _ = self.check_groups(&user, &policy)?;
}
Ok(Authorization {
permissions: policy.permissions.clone(),
user_id: user.id.clone(),
resource_id: resource_id.to_string(),
resource_type: self.resource_type.label.clone(),
expiration: Utc::now().naive_utc() + Duration::seconds(policy.duration.clone() as i64),
})
}
pub fn build(
&self,
resource_id: &str,
user: &UserAttributes,
) -> Result<Authorization, MinosError> {
if user.status != Status::Active {
return Err(MinosError::new(
ErrorKind::Authorization,
"The user is not active",
));
}
let mut permissions = vec![];
let mut durations = vec![];
for policy in &self.resource_type.policies {
match self.build_by_policy(&policy, &resource_id, &user) {
Ok(mut auth) => {
permissions.append(&mut auth.permissions);
durations.push(&policy.duration);
}
Err(_) => continue,
}
}
durations.sort();
let seconds = **durations
.get(0)
.ok_or(MinosError::new(ErrorKind::Authorization, "Not authorized"))?;
Ok(Authorization {
permissions,
user_id: user.id.clone(),
resource_id: resource_id.to_string(),
resource_type: self.resource_type.label.clone(),
expiration: Utc::now().naive_utc() + Duration::seconds(seconds as i64),
})
}
}