Browse Source

Split permissions into separate files

iorveth 4 years ago
parent
commit
9296239b50

+ 3 - 10
runtime-modules/content-directory/src/operations.rs

@@ -1,7 +1,4 @@
-use crate::{
-    InputPropertyValue, InputValue, PropertyId, SchemaId, Trait,
-    VecInputValue,
-};
+use crate::{InputPropertyValue, InputValue, PropertyId, SchemaId, Trait, VecInputValue};
 use codec::{Decode, Encode};
 use rstd::collections::btree_map::BTreeMap;
 use rstd::prelude::*;
@@ -90,9 +87,7 @@ pub fn parametrized_property_values_to_property_values<T: Trait>(
                 let entity_id = created_entities
                     .get(&op_index)
                     .ok_or("EntityNotCreatedByOperation")?;
-                InputPropertyValue::Single(InputValue::Reference(
-                    *entity_id,
-                ))
+                InputPropertyValue::Single(InputValue::Reference(*entity_id))
             }
             ParametrizedPropertyValue::InternalEntityVec(parametrized_entities) => {
                 let mut entities: Vec<T::EntityId> = vec![];
@@ -111,9 +106,7 @@ pub fn parametrized_property_values_to_property_values<T: Trait>(
                         }
                     }
                 }
-                InputPropertyValue::Vector(VecInputValue::Reference(
-                    entities,
-                ))
+                InputPropertyValue::Vector(VecInputValue::Reference(entities))
             }
         };
 

+ 12 - 487
runtime-modules/content-directory/src/permissions.rs

@@ -1,6 +1,16 @@
-use crate::errors::*;
+mod class;
+mod curator_group;
+mod entity;
+mod entity_creation_voucher;
+
+pub use class::*;
+pub use curator_group::*;
+pub use entity::*;
+pub use entity_creation_voucher::*;
+
+pub use crate::errors::*;
 use crate::*;
-use codec::{Codec, Decode, Encode};
+pub use codec::{Codec, Decode, Encode};
 use core::fmt::Debug;
 use runtime_primitives::traits::{MaybeSerializeDeserialize, Member, SimpleArithmetic};
 
@@ -98,182 +108,6 @@ pub fn ensure_is_lead<T: ActorAuthenticator>(origin: T::Origin) -> dispatch::Res
     ensure_lead_auth_success::<T>(&account_id)
 }
 
-/// Authorize curator, performing all checks to ensure curator can act
-pub fn perform_curator_in_group_auth<T: Trait>(
-    curator_id: &T::CuratorId,
-    curator_group_id: &T::CuratorGroupId,
-    account_id: &T::AccountId,
-) -> dispatch::Result {
-    // Ensure curator authorization performed succesfully
-    ensure_curator_auth_success::<T>(curator_id, account_id)?;
-
-    // Retrieve corresponding curator group
-    let curator_group = Module::<T>::curator_group_by_id(curator_group_id);
-
-    // Ensure curator group is active
-    ensure!(curator_group.is_active(), ERROR_CURATOR_GROUP_IS_NOT_ACTIVE);
-
-    // Ensure curator under given curator_id exists in CuratorGroup
-    CuratorGroup::<T>::ensure_curator_in_group_exists(&curator_group, curator_id)?;
-    Ok(())
-}
-
-/// A group, that consists of `curators` set
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub struct CuratorGroup<T: Trait> {
-    /// Curators set, associated with a iven curator group
-    curators: BTreeSet<T::CuratorId>,
-
-    /// When `false`, curator in a given group is forbidden to act
-    active: bool,
-
-    /// Used to count the number of `Class`(es), given curator group maintains
-    number_of_classes_maintained: u32,
-}
-
-impl<T: Trait> Default for CuratorGroup<T> {
-    fn default() -> Self {
-        Self {
-            curators: BTreeSet::new(),
-            // default curator group status right after creation
-            active: false,
-            number_of_classes_maintained: 0,
-        }
-    }
-}
-
-impl<T: Trait> CuratorGroup<T> {
-    /// Check if `CuratorGroup` contains curator under given `curator_id`
-    pub fn is_curator(&self, curator_id: &T::CuratorId) -> bool {
-        self.curators.contains(curator_id)
-    }
-
-    /// Check if `CuratorGroup` is active
-    pub fn is_active(&self) -> bool {
-        self.active
-    }
-
-    /// Set `CuratorGroup` status as provided
-    pub fn set_status(&mut self, is_active: bool) {
-        self.active = is_active
-    }
-
-    /// Retrieve set of all curator_ids related to `CuratorGroup` by reference
-    pub fn get_curators(&self) -> &BTreeSet<T::CuratorId> {
-        &self.curators
-    }
-
-    /// Retrieve set of all curator_ids related to `CuratorGroup` by mutable  reference
-    pub fn get_curators_mut(&mut self) -> &mut BTreeSet<T::CuratorId> {
-        &mut self.curators
-    }
-
-    /// Increment number of classes `CuratorGroup` maintains
-    pub fn increment_number_of_classes_maintained_count(&mut self) {
-        self.number_of_classes_maintained += 1;
-    }
-
-    /// Decrement number of classes `CuratorGroup` maintains
-    pub fn decrement_number_of_classes_maintained_count(&mut self) {
-        self.number_of_classes_maintained -= 1;
-    }
-
-    /// Ensure curator group does not maintain any `Class`
-    pub fn ensure_curator_group_maintains_no_classes(&self) -> dispatch::Result {
-        ensure!(
-            self.number_of_classes_maintained == 0,
-            ERROR_CURATOR_GROUP_REMOVAL_FORBIDDEN
-        );
-        Ok(())
-    }
-
-    /// Ensure `MaxNumberOfCuratorsPerGroup` constraint satisfied
-    pub fn ensure_max_number_of_curators_limit_not_reached(&self) -> dispatch::Result {
-        ensure!(
-            self.curators.len() < T::MaxNumberOfCuratorsPerGroup::get() as usize,
-            ERROR_NUMBER_OF_CURATORS_PER_GROUP_LIMIT_REACHED
-        );
-        Ok(())
-    }
-
-    /// Ensure curator under given `curator_id` exists in `CuratorGroup`
-    pub fn ensure_curator_in_group_exists(&self, curator_id: &T::CuratorId) -> dispatch::Result {
-        ensure!(
-            self.get_curators().contains(curator_id),
-            ERROR_CURATOR_IS_NOT_A_MEMBER_OF_A_GIVEN_CURATOR_GROUP
-        );
-        Ok(())
-    }
-}
-
-/// A voucher for `Entity` creation
-#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, Eq)]
-pub struct EntityCreationVoucher<T: Trait> {
-    /// How many are allowed in total
-    pub maximum_entities_count: T::EntityId,
-
-    /// How many have currently been created
-    pub entities_created: T::EntityId,
-}
-
-impl<T: Trait> Default for EntityCreationVoucher<T> {
-    fn default() -> Self {
-        Self {
-            maximum_entities_count: T::EntityId::zero(),
-            entities_created: T::EntityId::zero(),
-        }
-    }
-}
-
-impl<T: Trait> EntityCreationVoucher<T> {
-    /// Create a new instance of `EntityCreationVoucher` with specified limit
-    pub fn new(maximum_entities_count: T::EntityId) -> Self {
-        Self {
-            maximum_entities_count,
-            entities_created: T::EntityId::zero(),
-        }
-    }
-
-    /// Set new `maximum_entities_count` limit
-    pub fn set_maximum_entities_count(&mut self, maximum_entities_count: T::EntityId) {
-        self.maximum_entities_count = maximum_entities_count
-    }
-
-    /// Increase `entities_created` by 1
-    pub fn increment_created_entities_count(&mut self) {
-        self.entities_created += T::EntityId::one();
-    }
-
-    /// Decrease `entities_created` by 1
-    pub fn decrement_created_entities_count(&mut self) {
-        self.entities_created -= T::EntityId::one();
-    }
-
-    /// Check if `entities_created` is less than `maximum_entities_count` limit set to this `EntityCreationVoucher`
-    pub fn limit_not_reached(&self) -> bool {
-        self.entities_created < self.maximum_entities_count
-    }
-
-    /// Ensure new voucher`s max entities count is less than number of already created entities in this `EntityCreationVoucher`
-    pub fn ensure_new_max_entities_count_is_valid(
-        self,
-        maximum_entities_count: T::EntityId,
-    ) -> dispatch::Result {
-        ensure!(
-            maximum_entities_count >= self.entities_created,
-            ERROR_NEW_ENTITIES_MAX_COUNT_IS_LESS_THAN_NUMBER_OF_ALREADY_CREATED
-        );
-        Ok(())
-    }
-
-    /// Ensure voucher limit not reached
-    pub fn ensure_voucher_limit_not_reached(&self) -> dispatch::Result {
-        ensure!(self.limit_not_reached(), ERROR_VOUCHER_LIMIT_REACHED);
-        Ok(())
-    }
-}
-
 /// Enum, representing all possible `Actor`s
 #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
 #[derive(Encode, Decode, Eq, PartialEq, Clone, Copy, Debug)]
@@ -288,312 +122,3 @@ impl<T: Trait> Default for Actor<T> {
         Self::Lead
     }
 }
-
-/// Permissions for an instance of a `Class` in the versioned store.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
-pub struct ClassPermissions<T: Trait> {
-    /// For this permission, the individual member is allowed to create the entity and become controller.
-    any_member: bool,
-
-    /// Whether to prevent everyone from creating an entity.
-    ///
-    /// This could be useful in order to quickly, and possibly temporarily, block new entity creation, without
-    /// having to tear down `can_create_entities`.
-    entity_creation_blocked: bool,
-
-    /// Whether to prevent everyone from updating entity properties.
-    ///
-    /// This could be useful in order to quickly, and probably temporarily, block any editing of entities,
-    /// rather than for example having to set, and later clear.
-    all_entity_property_values_locked: bool,
-
-    /// Current class maintainer curator groups
-    maintainers: BTreeSet<T::CuratorGroupId>,
-}
-
-impl<T: Trait> Default for ClassPermissions<T> {
-    fn default() -> Self {
-        Self {
-            any_member: false,
-            entity_creation_blocked: false,
-            all_entity_property_values_locked: false,
-            maintainers: BTreeSet::new(),
-        }
-    }
-}
-
-impl<T: Trait> ClassPermissions<T> {
-    /// Retieve `all_entity_property_values_locked` status
-    pub fn all_entity_property_values_locked(&self) -> bool {
-        self.all_entity_property_values_locked
-    }
-
-    /// Retieve `any_member` status
-    pub fn any_member_status(&self) -> bool {
-        self.any_member
-    }
-
-    /// Check if given `curator_group_id` is maintainer of current `Class`
-    pub fn is_maintainer(&self, curator_group_id: &T::CuratorGroupId) -> bool {
-        self.maintainers.contains(curator_group_id)
-    }
-
-    /// Get `Class` maintainers by reference
-    pub fn get_maintainers(&self) -> &BTreeSet<T::CuratorGroupId> {
-        &self.maintainers
-    }
-
-    /// Get `Class` maintainers by mutable reference
-    pub fn get_maintainers_mut(&mut self) -> &mut BTreeSet<T::CuratorGroupId> {
-        &mut self.maintainers
-    }
-
-    /// Set `entity_creation_blocked` flag, as provided
-    pub fn set_entity_creation_blocked(&mut self, entity_creation_blocked: bool) {
-        self.entity_creation_blocked = entity_creation_blocked
-    }
-
-    /// Set `all_entity_property_values_locked` flag, as provided
-    pub fn set_all_entity_property_values_locked(
-        &mut self,
-        all_entity_property_values_locked: bool,
-    ) {
-        self.all_entity_property_values_locked = all_entity_property_values_locked
-    }
-
-    /// Set `any_member` flag, as provided
-    pub fn set_any_member_status(&mut self, any_member: bool) {
-        self.any_member = any_member;
-    }
-
-    /// Update `maintainers` set with provided one
-    pub fn set_maintainers(&mut self, maintainers: BTreeSet<T::CuratorGroupId>) {
-        self.maintainers = maintainers
-    }
-
-    /// Ensure provided actor can create entities of current `Class`
-    pub fn ensure_can_create_entities(
-        &self,
-        account_id: &T::AccountId,
-        actor: &Actor<T>,
-    ) -> dispatch::Result {
-        let can_create = match &actor {
-            Actor::Lead => {
-                // Ensure lead authorization performed succesfully
-                ensure_lead_auth_success::<T>(account_id)?;
-                true
-            }
-            Actor::Member(member_id) if self.any_member => {
-                // Ensure member authorization performed succesfully
-                ensure_member_auth_success::<T>(member_id, account_id)?;
-                true
-            }
-            Actor::Curator(curator_group_id, curator_id)
-                if self.maintainers.contains(curator_group_id) =>
-            {
-                // Authorize curator, performing all checks to ensure curator can act
-                perform_curator_in_group_auth::<T>(curator_id, curator_group_id, account_id)?;
-                true
-            }
-            _ => false,
-        };
-        ensure!(can_create, ERROR_ACTOR_CAN_NOT_CREATE_ENTITIES);
-        Ok(())
-    }
-
-    /// Ensure entities creation is not blocked on `Class` level
-    pub fn ensure_entity_creation_not_blocked(&self) -> dispatch::Result {
-        ensure!(!self.entity_creation_blocked, ERROR_ENTITY_CREATION_BLOCKED);
-        Ok(())
-    }
-
-    /// Ensure maintainer, associated with given `curator_group_id` is already added to `maintainers` set
-    pub fn ensure_maintainer_exists(
-        &self,
-        curator_group_id: &T::CuratorGroupId,
-    ) -> dispatch::Result {
-        ensure!(
-            self.maintainers.contains(curator_group_id),
-            ERROR_MAINTAINER_DOES_NOT_EXIST
-        );
-        Ok(())
-    }
-
-    /// Ensure maintainer, associated with given `curator_group_id` is not yet added to `maintainers` set
-    pub fn ensure_maintainer_does_not_exist(
-        &self,
-        curator_group_id: &T::CuratorGroupId,
-    ) -> dispatch::Result {
-        ensure!(
-            !self.maintainers.contains(curator_group_id),
-            ERROR_MAINTAINER_ALREADY_EXISTS
-        );
-        Ok(())
-    }
-}
-
-/// Owner of an `Entity`.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
-pub enum EntityController<T: Trait> {
-    Maintainers,
-    Member(T::MemberId),
-    Lead,
-}
-
-impl<T: Trait> EntityController<T> {
-    /// Create `EntityController` enum representation, using provided `Actor`
-    pub fn from_actor(actor: &Actor<T>) -> Self {
-        match &actor {
-            Actor::Lead => Self::Lead,
-            Actor::Member(member_id) => Self::Member(*member_id),
-            Actor::Curator(_, _) => Self::Maintainers,
-        }
-    }
-}
-
-impl<T: Trait> Default for EntityController<T> {
-    fn default() -> Self {
-        Self::Lead
-    }
-}
-
-/// Permissions for a given entity.
-#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
-pub struct EntityPermissions<T: Trait> {
-    /// Current controller, which is initially set based on who created entity
-    pub controller: EntityController<T>,
-
-    /// Forbid groups to mutate any property value.
-    /// Can be useful to use in concert with some curation censorship policy
-    pub frozen: bool,
-
-    /// Prevent from being referenced by any entity (including self-references).
-    /// Can be useful to use in concert with some curation censorship policy,
-    /// e.g. to block content from being included in some public playlist.
-    pub referenceable: bool,
-}
-
-impl<T: Trait> Default for EntityPermissions<T> {
-    fn default() -> Self {
-        Self {
-            controller: EntityController::<T>::default(),
-            frozen: false,
-            referenceable: true,
-        }
-    }
-}
-
-impl<T: Trait> EntityPermissions<T> {
-    /// Create an instance of `EntityPermissions` with `EntityController` equal to provided one
-    pub fn default_with_controller(controller: EntityController<T>) -> Self {
-        Self {
-            controller,
-            ..EntityPermissions::default()
-        }
-    }
-
-    /// Set current `controller` as provided
-    pub fn set_conroller(&mut self, controller: EntityController<T>) {
-        self.controller = controller
-    }
-
-    /// Check if inner `controller` is equal to the provided one
-    pub fn controller_is_equal_to(&self, new_entity_controller: &EntityController<T>) -> bool {
-        self.controller == *new_entity_controller
-    }
-
-    /// Set `frozen` flag as provided
-    pub fn set_frozen(&mut self, frozen: bool) {
-        self.frozen = frozen
-    }
-
-    /// Set `referenceable` flag as provided
-    pub fn set_referencable(&mut self, referenceable: bool) {
-        self.referenceable = referenceable;
-    }
-
-    /// Retrieve `referenceable` flag
-    pub fn is_referancable(&self) -> bool {
-        self.referenceable
-    }
-
-    /// Get current `controller` by reference
-    pub fn get_controller(&self) -> &EntityController<T> {
-        &self.controller
-    }
-
-    /// Ensure actor with given `EntityAccessLevel` can remove entity
-    pub fn ensure_group_can_remove_entity(access_level: EntityAccessLevel) -> dispatch::Result {
-        match access_level {
-            EntityAccessLevel::EntityController => Ok(()),
-            EntityAccessLevel::EntityControllerAndMaintainer => Ok(()),
-            _ => Err(ERROR_ENTITY_REMOVAL_ACCESS_DENIED),
-        }
-    }
-
-    /// Ensure provided new_entity_controller is not equal to current one
-    pub fn ensure_controllers_are_not_equal(
-        &self,
-        new_entity_controller: &EntityController<T>,
-    ) -> dispatch::Result {
-        ensure!(
-            !self.controller_is_equal_to(new_entity_controller),
-            ERROR_PROVIDED_ENTITY_CONTROLLER_IS_EQUAL_TO_CURRENT_ONE
-        );
-        Ok(())
-    }
-}
-
-/// Type, derived from dispatchable call, identifies the caller
-#[derive(Encode, Decode, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
-pub enum EntityAccessLevel {
-    /// Caller identified as the entity maintainer
-    EntityMaintainer,
-
-    /// Caller identified as the entity controller
-    EntityController,
-
-    /// Caller, that can act as controller and maintainer simultaneously
-    /// (can be useful, when controller and maintainer have features, that do not intersect)
-    EntityControllerAndMaintainer,
-}
-
-impl EntityAccessLevel {
-    /// Derives the `EntityAccessLevel` for the actor, attempting to act.
-    pub fn derive<T: Trait>(
-        account_id: &T::AccountId,
-        entity_permissions: &EntityPermissions<T>,
-        class_permissions: &ClassPermissions<T>,
-        actor: &Actor<T>,
-    ) -> Result<Self, &'static str> {
-        let controller = EntityController::<T>::from_actor(actor);
-        match actor {
-            Actor::Lead if entity_permissions.controller_is_equal_to(&controller) => {
-                // Ensure lead authorization performed succesfully
-                ensure_lead_auth_success::<T>(account_id).map(|_| Self::EntityController)
-            }
-            Actor::Member(member_id) if entity_permissions.controller_is_equal_to(&controller) => {
-                // Ensure member authorization performed succesfully
-                ensure_member_auth_success::<T>(member_id, account_id)
-                    .map(|_| Self::EntityController)
-            }
-            Actor::Curator(curator_group_id, curator_id) => {
-                // Authorize curator, performing all checks to ensure curator can act
-                perform_curator_in_group_auth::<T>(curator_id, curator_group_id, account_id)?;
-                match (
-                    entity_permissions.controller_is_equal_to(&controller),
-                    class_permissions.is_maintainer(curator_group_id),
-                ) {
-                    (true, true) => Ok(Self::EntityControllerAndMaintainer),
-                    (false, true) => Ok(Self::EntityMaintainer),
-                    // Curator cannot be controller, but not maintainer simultaneously
-                    _ => Err(ERROR_ENTITY_ACCESS_DENIED),
-                }
-            }
-            _ => Err(ERROR_ENTITY_ACCESS_DENIED),
-        }
-    }
-}

+ 149 - 0
runtime-modules/content-directory/src/permissions/class.rs

@@ -0,0 +1,149 @@
+use super::*;
+
+/// Permissions for an instance of a `Class` in the versioned store.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub struct ClassPermissions<T: Trait> {
+    /// For this permission, the individual member is allowed to create the entity and become controller.
+    any_member: bool,
+
+    /// Whether to prevent everyone from creating an entity.
+    ///
+    /// This could be useful in order to quickly, and possibly temporarily, block new entity creation, without
+    /// having to tear down `can_create_entities`.
+    entity_creation_blocked: bool,
+
+    /// Whether to prevent everyone from updating entity properties.
+    ///
+    /// This could be useful in order to quickly, and probably temporarily, block any editing of entities,
+    /// rather than for example having to set, and later clear.
+    all_entity_property_values_locked: bool,
+
+    /// Current class maintainer curator groups
+    maintainers: BTreeSet<T::CuratorGroupId>,
+}
+
+impl<T: Trait> Default for ClassPermissions<T> {
+    fn default() -> Self {
+        Self {
+            any_member: false,
+            entity_creation_blocked: false,
+            all_entity_property_values_locked: false,
+            maintainers: BTreeSet::new(),
+        }
+    }
+}
+
+impl<T: Trait> ClassPermissions<T> {
+    /// Retieve `all_entity_property_values_locked` status
+    pub fn all_entity_property_values_locked(&self) -> bool {
+        self.all_entity_property_values_locked
+    }
+
+    /// Retieve `any_member` status
+    pub fn any_member_status(&self) -> bool {
+        self.any_member
+    }
+
+    /// Check if given `curator_group_id` is maintainer of current `Class`
+    pub fn is_maintainer(&self, curator_group_id: &T::CuratorGroupId) -> bool {
+        self.maintainers.contains(curator_group_id)
+    }
+
+    /// Get `Class` maintainers by reference
+    pub fn get_maintainers(&self) -> &BTreeSet<T::CuratorGroupId> {
+        &self.maintainers
+    }
+
+    /// Get `Class` maintainers by mutable reference
+    pub fn get_maintainers_mut(&mut self) -> &mut BTreeSet<T::CuratorGroupId> {
+        &mut self.maintainers
+    }
+
+    /// Set `entity_creation_blocked` flag, as provided
+    pub fn set_entity_creation_blocked(&mut self, entity_creation_blocked: bool) {
+        self.entity_creation_blocked = entity_creation_blocked
+    }
+
+    /// Set `all_entity_property_values_locked` flag, as provided
+    pub fn set_all_entity_property_values_locked(
+        &mut self,
+        all_entity_property_values_locked: bool,
+    ) {
+        self.all_entity_property_values_locked = all_entity_property_values_locked
+    }
+
+    /// Set `any_member` flag, as provided
+    pub fn set_any_member_status(&mut self, any_member: bool) {
+        self.any_member = any_member;
+    }
+
+    /// Update `maintainers` set with provided one
+    pub fn set_maintainers(&mut self, maintainers: BTreeSet<T::CuratorGroupId>) {
+        self.maintainers = maintainers
+    }
+
+    /// Ensure provided actor can create entities of current `Class`
+    pub fn ensure_can_create_entities(
+        &self,
+        account_id: &T::AccountId,
+        actor: &Actor<T>,
+    ) -> dispatch::Result {
+        let can_create = match &actor {
+            Actor::Lead => {
+                // Ensure lead authorization performed succesfully
+                ensure_lead_auth_success::<T>(account_id)?;
+                true
+            }
+            Actor::Member(member_id) if self.any_member => {
+                // Ensure member authorization performed succesfully
+                ensure_member_auth_success::<T>(member_id, account_id)?;
+                true
+            }
+            Actor::Curator(curator_group_id, curator_id)
+                if self.maintainers.contains(curator_group_id) =>
+            {
+                // Authorize curator, performing all checks to ensure curator can act
+                CuratorGroup::<T>::perform_curator_in_group_auth(
+                    curator_id,
+                    curator_group_id,
+                    account_id,
+                )?;
+                true
+            }
+            _ => false,
+        };
+        ensure!(can_create, ERROR_ACTOR_CAN_NOT_CREATE_ENTITIES);
+        Ok(())
+    }
+
+    /// Ensure entities creation is not blocked on `Class` level
+    pub fn ensure_entity_creation_not_blocked(&self) -> dispatch::Result {
+        ensure!(!self.entity_creation_blocked, ERROR_ENTITY_CREATION_BLOCKED);
+        Ok(())
+    }
+
+    /// Ensure maintainer, associated with given `curator_group_id` is already added to `maintainers` set
+    pub fn ensure_maintainer_exists(
+        &self,
+        curator_group_id: &T::CuratorGroupId,
+    ) -> dispatch::Result {
+        ensure!(
+            self.maintainers.contains(curator_group_id),
+            ERROR_MAINTAINER_DOES_NOT_EXIST
+        );
+        Ok(())
+    }
+
+    /// Ensure maintainer, associated with given `curator_group_id` is not yet added to `maintainers` set
+    pub fn ensure_maintainer_does_not_exist(
+        &self,
+        curator_group_id: &T::CuratorGroupId,
+    ) -> dispatch::Result {
+        ensure!(
+            !self.maintainers.contains(curator_group_id),
+            ERROR_MAINTAINER_ALREADY_EXISTS
+        );
+        Ok(())
+    }
+}

+ 110 - 0
runtime-modules/content-directory/src/permissions/curator_group.rs

@@ -0,0 +1,110 @@
+use super::*;
+
+/// A group, that consists of `curators` set
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug)]
+pub struct CuratorGroup<T: Trait> {
+    /// Curators set, associated with a iven curator group
+    curators: BTreeSet<T::CuratorId>,
+
+    /// When `false`, curator in a given group is forbidden to act
+    active: bool,
+
+    /// Used to count the number of `Class`(es), given curator group maintains
+    number_of_classes_maintained: u32,
+}
+
+impl<T: Trait> Default for CuratorGroup<T> {
+    fn default() -> Self {
+        Self {
+            curators: BTreeSet::new(),
+            // default curator group status right after creation
+            active: false,
+            number_of_classes_maintained: 0,
+        }
+    }
+}
+
+impl<T: Trait> CuratorGroup<T> {
+    /// Check if `CuratorGroup` contains curator under given `curator_id`
+    pub fn is_curator(&self, curator_id: &T::CuratorId) -> bool {
+        self.curators.contains(curator_id)
+    }
+
+    /// Check if `CuratorGroup` is active
+    pub fn is_active(&self) -> bool {
+        self.active
+    }
+
+    /// Set `CuratorGroup` status as provided
+    pub fn set_status(&mut self, is_active: bool) {
+        self.active = is_active
+    }
+
+    /// Retrieve set of all curator_ids related to `CuratorGroup` by reference
+    pub fn get_curators(&self) -> &BTreeSet<T::CuratorId> {
+        &self.curators
+    }
+
+    /// Retrieve set of all curator_ids related to `CuratorGroup` by mutable  reference
+    pub fn get_curators_mut(&mut self) -> &mut BTreeSet<T::CuratorId> {
+        &mut self.curators
+    }
+
+    /// Increment number of classes `CuratorGroup` maintains
+    pub fn increment_number_of_classes_maintained_count(&mut self) {
+        self.number_of_classes_maintained += 1;
+    }
+
+    /// Decrement number of classes `CuratorGroup` maintains
+    pub fn decrement_number_of_classes_maintained_count(&mut self) {
+        self.number_of_classes_maintained -= 1;
+    }
+
+    /// Ensure curator group does not maintain any `Class`
+    pub fn ensure_curator_group_maintains_no_classes(&self) -> dispatch::Result {
+        ensure!(
+            self.number_of_classes_maintained == 0,
+            ERROR_CURATOR_GROUP_REMOVAL_FORBIDDEN
+        );
+        Ok(())
+    }
+
+    /// Ensure `MaxNumberOfCuratorsPerGroup` constraint satisfied
+    pub fn ensure_max_number_of_curators_limit_not_reached(&self) -> dispatch::Result {
+        ensure!(
+            self.curators.len() < T::MaxNumberOfCuratorsPerGroup::get() as usize,
+            ERROR_NUMBER_OF_CURATORS_PER_GROUP_LIMIT_REACHED
+        );
+        Ok(())
+    }
+
+    /// Ensure curator under given `curator_id` exists in `CuratorGroup`
+    pub fn ensure_curator_in_group_exists(&self, curator_id: &T::CuratorId) -> dispatch::Result {
+        ensure!(
+            self.get_curators().contains(curator_id),
+            ERROR_CURATOR_IS_NOT_A_MEMBER_OF_A_GIVEN_CURATOR_GROUP
+        );
+        Ok(())
+    }
+
+    /// Authorize curator, performing all checks to ensure curator can act
+    pub fn perform_curator_in_group_auth(
+        curator_id: &T::CuratorId,
+        curator_group_id: &T::CuratorGroupId,
+        account_id: &T::AccountId,
+    ) -> dispatch::Result {
+        // Ensure curator authorization performed succesfully
+        ensure_curator_auth_success::<T>(curator_id, account_id)?;
+
+        // Retrieve corresponding curator group
+        let curator_group = Module::<T>::curator_group_by_id(curator_group_id);
+
+        // Ensure curator group is active
+        ensure!(curator_group.is_active(), ERROR_CURATOR_GROUP_IS_NOT_ACTIVE);
+
+        // Ensure curator under given curator_id exists in CuratorGroup
+        Self::ensure_curator_in_group_exists(&curator_group, curator_id)?;
+        Ok(())
+    }
+}

+ 170 - 0
runtime-modules/content-directory/src/permissions/entity.rs

@@ -0,0 +1,170 @@
+use super::*;
+
+/// Owner of an `Entity`.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug)]
+pub enum EntityController<T: Trait> {
+    Maintainers,
+    Member(T::MemberId),
+    Lead,
+}
+
+impl<T: Trait> EntityController<T> {
+    /// Create `EntityController` enum representation, using provided `Actor`
+    pub fn from_actor(actor: &Actor<T>) -> Self {
+        match &actor {
+            Actor::Lead => Self::Lead,
+            Actor::Member(member_id) => Self::Member(*member_id),
+            Actor::Curator(_, _) => Self::Maintainers,
+        }
+    }
+}
+
+impl<T: Trait> Default for EntityController<T> {
+    fn default() -> Self {
+        Self::Lead
+    }
+}
+
+/// Permissions for a given entity.
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
+pub struct EntityPermissions<T: Trait> {
+    /// Current controller, which is initially set based on who created entity
+    pub controller: EntityController<T>,
+
+    /// Forbid groups to mutate any property value.
+    /// Can be useful to use in concert with some curation censorship policy
+    pub frozen: bool,
+
+    /// Prevent from being referenced by any entity (including self-references).
+    /// Can be useful to use in concert with some curation censorship policy,
+    /// e.g. to block content from being included in some public playlist.
+    pub referenceable: bool,
+}
+
+impl<T: Trait> Default for EntityPermissions<T> {
+    fn default() -> Self {
+        Self {
+            controller: EntityController::<T>::default(),
+            frozen: false,
+            referenceable: true,
+        }
+    }
+}
+
+impl<T: Trait> EntityPermissions<T> {
+    /// Create an instance of `EntityPermissions` with `EntityController` equal to provided one
+    pub fn default_with_controller(controller: EntityController<T>) -> Self {
+        Self {
+            controller,
+            ..EntityPermissions::default()
+        }
+    }
+
+    /// Set current `controller` as provided
+    pub fn set_conroller(&mut self, controller: EntityController<T>) {
+        self.controller = controller
+    }
+
+    /// Check if inner `controller` is equal to the provided one
+    pub fn controller_is_equal_to(&self, new_entity_controller: &EntityController<T>) -> bool {
+        self.controller == *new_entity_controller
+    }
+
+    /// Set `frozen` flag as provided
+    pub fn set_frozen(&mut self, frozen: bool) {
+        self.frozen = frozen
+    }
+
+    /// Set `referenceable` flag as provided
+    pub fn set_referencable(&mut self, referenceable: bool) {
+        self.referenceable = referenceable;
+    }
+
+    /// Retrieve `referenceable` flag
+    pub fn is_referancable(&self) -> bool {
+        self.referenceable
+    }
+
+    /// Get current `controller` by reference
+    pub fn get_controller(&self) -> &EntityController<T> {
+        &self.controller
+    }
+
+    /// Ensure actor with given `EntityAccessLevel` can remove entity
+    pub fn ensure_group_can_remove_entity(access_level: EntityAccessLevel) -> dispatch::Result {
+        match access_level {
+            EntityAccessLevel::EntityController => Ok(()),
+            EntityAccessLevel::EntityControllerAndMaintainer => Ok(()),
+            _ => Err(ERROR_ENTITY_REMOVAL_ACCESS_DENIED),
+        }
+    }
+
+    /// Ensure provided new_entity_controller is not equal to current one
+    pub fn ensure_controllers_are_not_equal(
+        &self,
+        new_entity_controller: &EntityController<T>,
+    ) -> dispatch::Result {
+        ensure!(
+            !self.controller_is_equal_to(new_entity_controller),
+            ERROR_PROVIDED_ENTITY_CONTROLLER_IS_EQUAL_TO_CURRENT_ONE
+        );
+        Ok(())
+    }
+}
+
+/// Type, derived from dispatchable call, identifies the caller
+#[derive(Encode, Decode, Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
+pub enum EntityAccessLevel {
+    /// Caller identified as the entity maintainer
+    EntityMaintainer,
+
+    /// Caller identified as the entity controller
+    EntityController,
+
+    /// Caller, that can act as controller and maintainer simultaneously
+    /// (can be useful, when controller and maintainer have features, that do not intersect)
+    EntityControllerAndMaintainer,
+}
+
+impl EntityAccessLevel {
+    /// Derives the `EntityAccessLevel` for the actor, attempting to act.
+    pub fn derive<T: Trait>(
+        account_id: &T::AccountId,
+        entity_permissions: &EntityPermissions<T>,
+        class_permissions: &ClassPermissions<T>,
+        actor: &Actor<T>,
+    ) -> Result<Self, &'static str> {
+        let controller = EntityController::<T>::from_actor(actor);
+        match actor {
+            Actor::Lead if entity_permissions.controller_is_equal_to(&controller) => {
+                // Ensure lead authorization performed succesfully
+                ensure_lead_auth_success::<T>(account_id).map(|_| Self::EntityController)
+            }
+            Actor::Member(member_id) if entity_permissions.controller_is_equal_to(&controller) => {
+                // Ensure member authorization performed succesfully
+                ensure_member_auth_success::<T>(member_id, account_id)
+                    .map(|_| Self::EntityController)
+            }
+            Actor::Curator(curator_group_id, curator_id) => {
+                // Authorize curator, performing all checks to ensure curator can act
+                CuratorGroup::<T>::perform_curator_in_group_auth(
+                    curator_id,
+                    curator_group_id,
+                    account_id,
+                )?;
+                match (
+                    entity_permissions.controller_is_equal_to(&controller),
+                    class_permissions.is_maintainer(curator_group_id),
+                ) {
+                    (true, true) => Ok(Self::EntityControllerAndMaintainer),
+                    (false, true) => Ok(Self::EntityMaintainer),
+                    // Curator cannot be controller, but not maintainer simultaneously
+                    _ => Err(ERROR_ENTITY_ACCESS_DENIED),
+                }
+            }
+            _ => Err(ERROR_ENTITY_ACCESS_DENIED),
+        }
+    }
+}

+ 68 - 0
runtime-modules/content-directory/src/permissions/entity_creation_voucher.rs

@@ -0,0 +1,68 @@
+use super::*;
+
+/// A voucher for `Entity` creation
+#[derive(Encode, Decode, Clone, Copy, Debug, PartialEq, Eq)]
+pub struct EntityCreationVoucher<T: Trait> {
+    /// How many are allowed in total
+    pub maximum_entities_count: T::EntityId,
+
+    /// How many have currently been created
+    pub entities_created: T::EntityId,
+}
+
+impl<T: Trait> Default for EntityCreationVoucher<T> {
+    fn default() -> Self {
+        Self {
+            maximum_entities_count: T::EntityId::zero(),
+            entities_created: T::EntityId::zero(),
+        }
+    }
+}
+
+impl<T: Trait> EntityCreationVoucher<T> {
+    /// Create a new instance of `EntityCreationVoucher` with specified limit
+    pub fn new(maximum_entities_count: T::EntityId) -> Self {
+        Self {
+            maximum_entities_count,
+            entities_created: T::EntityId::zero(),
+        }
+    }
+
+    /// Set new `maximum_entities_count` limit
+    pub fn set_maximum_entities_count(&mut self, maximum_entities_count: T::EntityId) {
+        self.maximum_entities_count = maximum_entities_count
+    }
+
+    /// Increase `entities_created` by 1
+    pub fn increment_created_entities_count(&mut self) {
+        self.entities_created += T::EntityId::one();
+    }
+
+    /// Decrease `entities_created` by 1
+    pub fn decrement_created_entities_count(&mut self) {
+        self.entities_created -= T::EntityId::one();
+    }
+
+    /// Check if `entities_created` is less than `maximum_entities_count` limit set to this `EntityCreationVoucher`
+    pub fn limit_not_reached(&self) -> bool {
+        self.entities_created < self.maximum_entities_count
+    }
+
+    /// Ensure new voucher`s max entities count is less than number of already created entities in this `EntityCreationVoucher`
+    pub fn ensure_new_max_entities_count_is_valid(
+        self,
+        maximum_entities_count: T::EntityId,
+    ) -> dispatch::Result {
+        ensure!(
+            maximum_entities_count >= self.entities_created,
+            ERROR_NEW_ENTITIES_MAX_COUNT_IS_LESS_THAN_NUMBER_OF_ALREADY_CREATED
+        );
+        Ok(())
+    }
+
+    /// Ensure voucher limit not reached
+    pub fn ensure_voucher_limit_not_reached(&self) -> dispatch::Result {
+        ensure!(self.limit_not_reached(), ERROR_VOUCHER_LIMIT_REACHED);
+        Ok(())
+    }
+}

+ 33 - 53
runtime-modules/content-directory/src/schema.rs

@@ -454,8 +454,7 @@ impl<T: Trait> Property<T> {
         &self,
         value: &InputPropertyValue<T>,
     ) -> dispatch::Result {
-        let single_value = value
-            .as_single_value();
+        let single_value = value.as_single_value();
 
         match (single_value, &self.property_type.as_single_value_type()) {
             (Some(InputValue::Text(text)), Some(Type::Text(text_max_len))) => {
@@ -501,11 +500,9 @@ impl<T: Trait> Property<T> {
         &self,
         value: &InputPropertyValue<T>,
     ) -> dispatch::Result {
-        let (vec_value, vec_property_type) = if let (Some(vec_value), Some(vec_property_type)) = (
-            value
-                .as_vec_value(),
-            self.property_type.as_vec_type(),
-        ) {
+        let (vec_value, vec_property_type) = if let (Some(vec_value), Some(vec_property_type)) =
+            (value.as_vec_value(), self.property_type.as_vec_type())
+        {
             (vec_value, vec_property_type)
         } else {
             return Ok(());
@@ -568,43 +565,33 @@ impl<T: Trait> Property<T> {
             (
                 InputPropertyValue::Single(single_property_value),
                 PropertyType::Single(ref single_property_type),
-            ) => {
-                match (
-                    single_property_value,
-                    single_property_type.deref(),
-                ) {
-                    (InputValue::Bool(_), Type::Bool)
-                    | (InputValue::Uint16(_), Type::Uint16)
-                    | (InputValue::Uint32(_), Type::Uint32)
-                    | (InputValue::Uint64(_), Type::Uint64)
-                    | (InputValue::Int16(_), Type::Int16)
-                    | (InputValue::Int32(_), Type::Int32)
-                    | (InputValue::Int64(_), Type::Int64)
-                    | (InputValue::Text(_), Type::Text(_))
-                    | (InputValue::Reference(_), Type::Reference(_, _)) => true,
-                    _ => false,
-                }
-            }
+            ) => match (single_property_value, single_property_type.deref()) {
+                (InputValue::Bool(_), Type::Bool)
+                | (InputValue::Uint16(_), Type::Uint16)
+                | (InputValue::Uint32(_), Type::Uint32)
+                | (InputValue::Uint64(_), Type::Uint64)
+                | (InputValue::Int16(_), Type::Int16)
+                | (InputValue::Int32(_), Type::Int32)
+                | (InputValue::Int64(_), Type::Int64)
+                | (InputValue::Text(_), Type::Text(_))
+                | (InputValue::Reference(_), Type::Reference(_, _)) => true,
+                _ => false,
+            },
             (
                 InputPropertyValue::Vector(vec_value),
                 PropertyType::Vector(ref vec_property_type),
-            ) => {
-                match (
-                    vec_value,
-                    vec_property_type.get_vec_type(),
-                ) {
-                    (VecInputValue::Bool(_), Type::Bool)
-                    | (VecInputValue::Uint16(_), Type::Uint16)
-                    | (VecInputValue::Uint32(_), Type::Uint32)
-                    | (VecInputValue::Uint64(_), Type::Uint64)
-                    | (VecInputValue::Int16(_), Type::Int16)
-                    | (VecInputValue::Int32(_), Type::Int32)
-                    | (VecInputValue::Int64(_), Type::Int64)
-                    | (VecInputValue::Text(_), Type::Text(_))
-                    | (VecInputValue::Reference(_), Type::Reference(_, _)) => true,
-                    _ => false,
-                }
-            }
+            ) => match (vec_value, vec_property_type.get_vec_type()) {
+                (VecInputValue::Bool(_), Type::Bool)
+                | (VecInputValue::Uint16(_), Type::Uint16)
+                | (VecInputValue::Uint32(_), Type::Uint32)
+                | (VecInputValue::Uint64(_), Type::Uint64)
+                | (VecInputValue::Int16(_), Type::Int16)
+                | (VecInputValue::Int32(_), Type::Int32)
+                | (VecInputValue::Int64(_), Type::Int64)
+                | (VecInputValue::Text(_), Type::Text(_))
+                | (VecInputValue::Reference(_), Type::Reference(_, _)) => true,
+                _ => false,
+            },
             _ => false,
         }
     }
@@ -624,10 +611,8 @@ impl<T: Trait> Property<T> {
                 if let (
                     InputValue::Reference(entity_id),
                     Type::Reference(class_id, same_controller_status),
-                ) = (
-                    single_property_value,
-                    single_property_type.deref(),
-                ) {
+                ) = (single_property_value, single_property_type.deref())
+                {
                     // Ensure class_id of Entity under provided entity_id references Entity,
                     // which class_id is equal to class_id, declared in corresponding PropertyType
                     // Retrieve corresponding Entity
@@ -642,17 +627,12 @@ impl<T: Trait> Property<T> {
                     )?;
                 }
             }
-            (
-                InputPropertyValue::Vector(vec_value),
-                PropertyType::Vector(vec_property_type),
-            ) => {
+            (InputPropertyValue::Vector(vec_value), PropertyType::Vector(vec_property_type)) => {
                 if let (
                     VecInputValue::Reference(entity_ids),
                     Type::Reference(class_id, same_controller_status),
-                ) = (
-                    vec_value,
-                    vec_property_type.get_vec_type(),
-                ) {
+                ) = (vec_value, vec_property_type.get_vec_type())
+                {
                     for entity_id in entity_ids.iter() {
                         // Ensure class_id of Entity under provided entity_id references Entity,
                         // which class_id is equal to class_id, declared in corresponding PropertyType