use crate::error::{NeptAuthError, NeptAuthResult};
use cosmwasm_std::{Addr, CustomQuery, Deps, Empty, Env};
use std::fmt::Debug;
#[derive(Clone, Debug)]
pub enum PermissionGroup {
Public,
Restricted(Vec<Addr>),
}
impl From<Vec<Addr>> for PermissionGroup {
fn from(vec: Vec<Addr>) -> Self {
Self::Restricted(vec)
}
}
pub type PermissionGroupList<'a, C> = Vec<&'a dyn GetPermissionGroup<C>>;
pub trait NeptuneAuth {
fn permissions<C: CustomQuery>(&self) -> NeptAuthResult<PermissionGroupList<C>>;
fn neptune_authorize(
&self,
deps: Deps<impl CustomQuery>,
env: &Env,
address: &Addr,
) -> NeptAuthResult<()> {
let permissions = self.permissions()?;
authorize_permissions(deps, env, address, &permissions)
}
}
pub trait GetPermissionGroup<C = Empty>: Debug
where
C: CustomQuery,
{
fn get_permission_group(&self, deps: Deps<C>, env: &Env) -> NeptAuthResult<PermissionGroup>;
}
#[derive(Clone, Debug)]
pub enum BasePermissionGroups {
Internal,
Public,
}
impl<C> GetPermissionGroup<C> for BasePermissionGroups
where
C: CustomQuery,
{
fn get_permission_group(&self, _deps: Deps<C>, env: &Env) -> NeptAuthResult<PermissionGroup> {
Ok(match self {
Self::Internal => PermissionGroup::Restricted(vec![env.contract.address.clone()]),
Self::Public => PermissionGroup::Public,
})
}
}
pub fn authorize_permissions<C: CustomQuery>(
deps: Deps<C>,
env: &Env,
addr: &Addr,
permissions: &PermissionGroupList<C>,
) -> NeptAuthResult<()> {
let collected_permissions = permissions
.iter()
.map(|x| x.get_permission_group(deps, env))
.collect::<Result<Vec<_>, _>>()?;
let flattened = flatten_permissions(collected_permissions)?;
match flattened {
PermissionGroup::Public => Ok(()),
PermissionGroup::Restricted(vec) => {
if vec.contains(addr) {
Ok(())
} else {
Err(NeptAuthError::Unauthorized {
sender: addr.clone(),
permission_group: format!("{permissions:?}"),
})
}
}
}
}
fn flatten_permissions(
permission_group_vec: Vec<PermissionGroup>,
) -> NeptAuthResult<PermissionGroup> {
if permission_group_vec.is_empty() {
Err(NeptAuthError::EmptyPermissionGroupList)
} else if permission_group_vec.len() == 1 {
Ok(permission_group_vec[0].clone())
} else {
let mut result_vec: Vec<Addr> = vec![];
for permission_group in permission_group_vec {
match permission_group {
PermissionGroup::Public => return Err(NeptAuthError::InvalidPublic),
PermissionGroup::Restricted(vec) => result_vec = [result_vec, vec].concat(),
}
}
return Ok(PermissionGroup::Restricted(result_vec));
}
}