use super::*;
use frame_support::pallet_prelude::*;
use frame_support::{sp_io::hashing::blake2_256, sp_std::borrow::ToOwned};
use sp_runtime::sp_std::vec::Vec;
use crate::types::*;
impl<T: Config> RoleBasedAccessControl<T::AccountId> for Pallet<T> {
fn create_scope(pallet: IdOrVec, scope_id: ScopeId) -> DispatchResult {
let pallet_id = pallet.to_id();
<Scopes<T>>::try_mutate(pallet_id, |scopes| {
ensure!(!scopes.contains(&scope_id), Error::<T>::ScopeAlreadyExists);
scopes.try_push(scope_id).map_err(|_| Error::<T>::ExceedMaxScopesPerPallet)?;
Ok(())
})
}
fn remove_scope(pallet: IdOrVec, scope_id: ScopeId) -> DispatchResult {
let pallet_id = pallet.to_id();
<Scopes<T>>::try_mutate_exists::<_, (), DispatchError, _>(pallet_id, |scopes_option| {
let scopes = scopes_option.as_mut().ok_or(Error::<T>::ScopeNotFound)?;
let s_pos = scopes.iter().position(|&s| s == scope_id).ok_or(Error::<T>::ScopeNotFound)?;
scopes.remove(s_pos);
if scopes.is_empty() {
scopes_option.clone_from(&None);
}
Ok(())
})?;
let mut scope_users = <UsersByScope<T>>::iter_prefix((pallet_id, scope_id))
.flat_map(|(_role, users)| users)
.collect::<Vec<_>>();
scope_users.sort();
scope_users.dedup();
scope_users.iter().for_each(|user| {
<RolesByUser<T>>::remove((user, pallet_id, scope_id));
});
let _ = <UsersByScope<T>>::clear_prefix((pallet_id, scope_id), 1000, None);
Ok(())
}
fn remove_pallet_storage(pallet: IdOrVec) -> DispatchResult {
let pallet_id_enum = pallet.to_id_enum();
let pallet_id = pallet_id_enum.to_id();
let scopes = <Scopes<T>>::get(pallet_id);
for scope in scopes {
Self::remove_scope(pallet_id_enum.clone(), scope)?;
}
let pallet_roles = <PalletRoles<T>>::take(pallet_id);
let all_pallet_roles =
<PalletRoles<T>>::iter().map(|p| p.1.to_vec()).collect::<Vec<Vec<[u8; 32]>>>();
let flatten_all_pallet_roles = all_pallet_roles.iter().flatten().collect::<Vec<&[u8; 32]>>();
let filtered_roles = pallet_roles
.iter()
.filter(|pallet_role| !flatten_all_pallet_roles.contains(pallet_role));
filtered_roles.for_each(|role| {
<Roles<T>>::remove(role);
});
let _ = <PermissionsByRole<T>>::clear_prefix(pallet_id, 1000, None);
let _ = <Permissions<T>>::clear_prefix(pallet_id, 1000, None);
Ok(())
}
fn create_and_set_roles(
pallet: IdOrVec,
roles: Vec<Vec<u8>>,
) -> Result<BoundedVec<RoleId, T::MaxRolesPerPallet>, DispatchError> {
let mut role_ids = Vec::<[u8; 32]>::new();
for role in roles {
role_ids.push(Self::create_role(role.to_owned())?);
}
Self::set_multiple_pallet_roles(pallet.to_id_enum(), role_ids.clone())?;
let bounded_ids = Self::bound(role_ids, Error::<T>::ExceedMaxRolesPerPallet)?;
Self::deposit_event(Event::RolesStored(pallet.to_id(), bounded_ids.clone()));
Ok(bounded_ids)
}
fn create_role(role: Vec<u8>) -> Result<RoleId, DispatchError> {
let role_id = role.using_encoded(blake2_256);
let b_role = Self::bound::<_, T::RoleMaxLen>(role, Error::<T>::ExceedRoleMaxLen)?;
ensure!(role_id == b_role.using_encoded(blake2_256), Error::<T>::NoneValue);
if !<Roles<T>>::contains_key(role_id) {
<Roles<T>>::insert(role_id, b_role)
};
Ok(role_id)
}
fn set_role_to_pallet(pallet: IdOrVec, role_id: RoleId) -> DispatchResult {
ensure!(<Roles<T>>::contains_key(role_id), Error::<T>::RoleNotFound);
<PalletRoles<T>>::try_mutate(pallet.to_id(), |roles| {
ensure!(!roles.contains(&role_id), Error::<T>::RoleAlreadyLinkedToPallet);
roles.try_push(role_id).map_err(|_| Error::<T>::ExceedMaxRolesPerPallet)
})?;
Ok(())
}
fn set_multiple_pallet_roles(pallet: IdOrVec, roles: Vec<RoleId>) -> DispatchResult {
let pallet_id = pallet.to_id();
ensure!(Self::has_unique_elements(roles.clone()), Error::<T>::DuplicateRole);
let pallet_roles = <PalletRoles<T>>::get(&pallet_id);
for id in roles.clone() {
ensure!(!pallet_roles.contains(&id), Error::<T>::RoleAlreadyLinkedToPallet);
}
<PalletRoles<T>>::try_mutate(pallet_id, |pallet_roles| {
pallet_roles.try_extend(roles.into_iter())
})
.map_err(|_| Error::<T>::ExceedMaxRolesPerPallet)?;
Ok(())
}
fn assign_role_to_user(
user: T::AccountId,
pallet: IdOrVec,
scope_id: &ScopeId,
role_id: RoleId,
) -> DispatchResult {
let pallet_id_enum = pallet.to_id_enum();
let pallet_id = pallet_id_enum.to_id();
Self::scope_exists(pallet_id_enum.clone(), scope_id)?;
Self::is_role_linked_to_pallet(pallet_id_enum, &role_id)?;
<RolesByUser<T>>::try_mutate((&user, pallet_id, scope_id), |roles| {
ensure!(!roles.contains(&role_id), Error::<T>::UserAlreadyHasRole);
roles.try_push(role_id).map_err(|_| Error::<T>::ExceedMaxRolesPerUser)
})?;
<UsersByScope<T>>::try_mutate((pallet_id, scope_id, role_id), |users| {
ensure!(!users.contains(&user), Error::<T>::UserAlreadyHasRole);
users.try_push(user.clone()).map_err(|_| Error::<T>::ExceedMaxUsersPerRole)
})?;
Self::deposit_event(Event::RoleAssignedToUser(pallet_id, scope_id.to_owned(), role_id, user));
Ok(())
}
fn remove_role_from_user(
user: T::AccountId,
pallet: IdOrVec,
scope_id: &ScopeId,
role_id: RoleId,
) -> DispatchResult {
let pallet_id = pallet.to_id();
<RolesByUser<T>>::try_mutate_exists::<_, (), DispatchError, _>(
(user.clone(), pallet_id, scope_id),
|user_roles_option| {
let user_roles = user_roles_option.as_mut().ok_or(Error::<T>::UserHasNoRoles)?;
let r_pos =
user_roles.iter().position(|&r| r == role_id).ok_or(Error::<T>::RoleNotFound)?;
user_roles.remove(r_pos);
if user_roles.is_empty() {
user_roles_option.clone_from(&None)
}
Ok(())
},
)?;
<UsersByScope<T>>::try_mutate_exists::<_, (), DispatchError, _>(
(pallet_id, scope_id, role_id),
|auth_users_option| {
let auth_users = auth_users_option.as_mut().ok_or(Error::<T>::RoleHasNoUsers)?;
let u_pos = auth_users.iter().position(|u| *u == user).ok_or(Error::<T>::UserNotFound)?;
auth_users.remove(u_pos);
if auth_users.is_empty() {
auth_users_option.clone_from(&None);
}
Ok(())
},
)?;
Self::deposit_event(Event::RoleRemovedFromUser(pallet_id, scope_id.to_owned(), role_id, user));
Ok(())
}
fn create_and_set_permissions(
pallet: IdOrVec,
role_id: RoleId,
permissions: Vec<Vec<u8>>,
) -> Result<BoundedVec<PermissionId, Self::MaxPermissionsPerRole>, DispatchError> {
ensure!(Self::has_unique_elements(permissions.clone()), Error::<T>::DuplicatePermission);
let pallet_id_enum = pallet.to_id_enum();
Self::is_role_linked_to_pallet(pallet_id_enum.clone(), &role_id)?;
let mut permission_ids = Vec::<[u8; 32]>::new();
for permission in permissions {
permission_ids.push(Self::create_permission(pallet_id_enum.clone(), permission.to_owned())?);
}
Self::set_multiple_permissions_to_role(pallet_id_enum, role_id, permission_ids.clone())?;
let b_permissions = Self::bound(permission_ids, Error::<T>::ExceedMaxPermissionsPerRole)?;
Self::deposit_event(Event::PermissionsCreatedAndSet(
pallet.to_id(),
role_id,
b_permissions.clone(),
));
Ok(b_permissions)
}
fn create_permission(
pallet: IdOrVec,
permission: Vec<u8>,
) -> Result<PermissionId, DispatchError> {
let permission_id = Self::to_id(permission.clone());
let pallet_id = pallet.to_id();
let b_permission =
Self::bound::<_, T::PermissionMaxLen>(permission, Error::<T>::ExceedPermissionMaxLen)?;
if !<Permissions<T>>::contains_key(pallet_id, permission_id) {
<Permissions<T>>::insert(pallet_id, permission_id, b_permission);
}
Ok(permission_id)
}
fn set_permission_to_role(
pallet: IdOrVec,
role_id: RoleId,
permission_id: PermissionId,
) -> DispatchResult {
let pallet_id_enum = pallet.to_id_enum();
let pallet_id = pallet_id_enum.to_id();
Self::is_role_linked_to_pallet(pallet_id_enum, &role_id)?;
ensure!(
<Permissions<T>>::contains_key(pallet_id, permission_id),
Error::<T>::PermissionNotFound
);
<PermissionsByRole<T>>::try_mutate(pallet_id, role_id, |role_permissions| {
ensure!(!role_permissions.contains(&permission_id), Error::<T>::DuplicatePermission);
role_permissions
.try_push(permission_id)
.map_err(|_| Error::<T>::ExceedMaxPermissionsPerRole)
})?;
Ok(())
}
fn set_multiple_permissions_to_role(
pallet: IdOrVec,
role_id: RoleId,
permissions: Vec<PermissionId>,
) -> DispatchResult {
ensure!(Self::has_unique_elements(permissions.clone()), Error::<T>::DuplicatePermission);
let pallet_id_enum = pallet.to_id_enum();
let pallet_id = pallet_id_enum.to_id();
Self::is_role_linked_to_pallet(pallet_id_enum, &role_id)?;
let role_permissions = <PermissionsByRole<T>>::get(&pallet_id, role_id);
for id in permissions.clone() {
ensure!(!role_permissions.contains(&id), Error::<T>::PermissionAlreadyLinkedToRole);
}
<PermissionsByRole<T>>::try_mutate(pallet_id, role_id, |role_permissions| {
role_permissions.try_extend(permissions.into_iter())
})
.map_err(|_| Error::<T>::ExceedMaxPermissionsPerRole)?;
Ok(())
}
fn do_revoke_permission_from_role(
pallet: IdOrVec,
role_id: RoleId,
permission_id: PermissionId,
) -> DispatchResult {
Self::permission_exists(pallet.clone(), &permission_id)?;
Self::is_role_linked_to_pallet(pallet.clone(), &role_id)?;
Self::is_permission_linked_to_role(pallet.clone(), &role_id, &permission_id)?;
<PermissionsByRole<T>>::try_mutate::<_, _, _, DispatchError, _>(
pallet.to_id(),
role_id,
|role_permissions| {
let p_index = role_permissions
.iter()
.position(|&p| p == permission_id)
.ok_or(Error::<T>::PermissionNotLinkedToRole)?;
role_permissions.remove(p_index);
Ok(())
},
)?;
Self::deposit_event(Event::PermissionRevokedFromRole(pallet.to_id(), role_id, permission_id));
Ok(())
}
fn do_remove_permission_from_pallet(pallet: IdOrVec, permission: PermissionId) -> DispatchResult {
Self::permission_exists(pallet.clone(), &permission)?;
let pallet_id = pallet.to_id();
let affected_roles: Vec<RoleId> = Self::get_roles_that_have_permission(pallet_id, &permission);
affected_roles.iter().for_each(|role| {
<PermissionsByRole<T>>::mutate(pallet_id, role, |permissions| {
permissions.retain(|&p| p != permission)
})
});
<Permissions<T>>::remove(pallet_id, permission);
Self::deposit_event(Event::PermissionRemovedFromPallet(
pallet_id,
permission,
Self::bound(affected_roles, Error::<T>::ExceedMaxRolesPerPallet)?, ));
Ok(())
}
fn is_authorized(
user: T::AccountId,
pallet: IdOrVec,
scope_id: &ScopeId,
permission_id: &PermissionId,
) -> DispatchResult {
let pallet_id_enum = pallet.to_id_enum();
let pallet_id = pallet_id_enum.to_id();
Self::scope_exists(pallet_id_enum.clone(), scope_id)?;
Self::permission_exists(pallet_id_enum, permission_id)?;
let user_roles = <RolesByUser<T>>::get((user, pallet_id, scope_id));
let has_permission = user_roles
.iter()
.any(|r_id| <PermissionsByRole<T>>::get(pallet_id, r_id).contains(permission_id));
ensure!(has_permission, Error::<T>::NotAuthorized);
Ok(())
}
fn has_role(
user: T::AccountId,
pallet: IdOrVec,
scope_id: &ScopeId,
role_ids: Vec<RoleId>,
) -> DispatchResult {
let pallet_id_enum = pallet.to_id_enum();
Self::scope_exists(pallet_id_enum.clone(), scope_id)?;
let user_roles = <RolesByUser<T>>::get((user, pallet_id_enum.to_id(), scope_id));
ensure!(user_roles.iter().any(|r| role_ids.contains(r)), Error::<T>::NotAuthorized);
Ok(())
}
fn does_user_have_any_role_in_scope(
account: T::AccountId,
pallet: IdOrVec,
scope_id: &ScopeId,
) -> bool {
let pallet_id = pallet.to_id();
UsersByScope::<T>::iter_prefix((pallet_id, scope_id)).any(|(_, users)| users.contains(&account))
}
fn scope_exists(pallet: IdOrVec, scope_id: &ScopeId) -> DispatchResult {
ensure!(<Scopes<T>>::get(pallet.to_id()).contains(scope_id), Error::<T>::ScopeNotFound);
Ok(())
}
fn permission_exists(pallet: IdOrVec, permission_id: &PermissionId) -> DispatchResult {
ensure!(
<Permissions<T>>::contains_key(pallet.to_id(), permission_id),
Error::<T>::PermissionNotFound
);
Ok(())
}
fn is_role_linked_to_pallet(pallet: IdOrVec, role_id: &RoleId) -> DispatchResult {
<PalletRoles<T>>::get(pallet.to_id())
.iter()
.find(|pallet_role| *pallet_role == role_id)
.ok_or(Error::<T>::RoleNotLinkedToPallet)?;
Ok(())
}
fn is_permission_linked_to_role(
pallet: IdOrVec,
role_id: &RoleId,
permission_id: &PermissionId,
) -> DispatchResult {
let role_permissions = <PermissionsByRole<T>>::get(pallet.to_id(), role_id);
ensure!(role_permissions.contains(permission_id), Error::<T>::PermissionNotLinkedToRole);
Ok(())
}
fn get_roles_that_have_permission(
pallet_id: PalletId,
permission_id: &PermissionId,
) -> Vec<RoleId> {
<PermissionsByRole<T>>::iter_prefix(pallet_id)
.filter_map(|(role, permissions)| permissions.contains(permission_id).then(|| role))
.collect()
}
fn get_role_users_len(pallet: IdOrVec, scope_id: &ScopeId, role_id: &RoleId) -> usize {
<UsersByScope<T>>::get((pallet.to_id(), scope_id, role_id)).len()
}
fn to_id(v: Vec<u8>) -> [u8; 32] {
v.using_encoded(blake2_256)
}
fn get_roles_by_user(user: T::AccountId, pallet: IdOrVec, scope_id: &ScopeId) -> Vec<RoleId> {
<RolesByUser<T>>::get((user, pallet.to_id(), scope_id)).into()
}
type MaxRolesPerPallet = T::MaxRolesPerPallet;
type MaxPermissionsPerRole = T::MaxPermissionsPerRole;
type PermissionMaxLen = T::PermissionMaxLen;
type RoleMaxLen = T::RoleMaxLen;
}
impl<T: Config> Pallet<T> {
fn bound<E, Len: Get<u32>>(vec: Vec<E>, err: Error<T>) -> Result<BoundedVec<E, Len>, Error<T>> {
BoundedVec::<E, Len>::try_from(vec).map_err(|_| err)
}
fn has_unique_elements<E: Ord + Clone>(vec: Vec<E>) -> bool {
let mut filtered_vec = vec.clone();
filtered_vec.sort();
filtered_vec.dedup();
vec.len() == filtered_vec.len()
}
}