Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

frame-support: Introduce EnsureOriginOrHigherPrivilege #12844

Merged
merged 2 commits into from
Dec 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions frame/support/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ mod dispatch;
pub use dispatch::EnsureOneOf;
pub use dispatch::{
AsEnsureOriginWithArg, CallerTrait, EitherOf, EitherOfDiverse, EnsureOrigin,
EnsureOriginWithArg, MapSuccess, NeverEnsureOrigin, OriginTrait, TryMapSuccess,
UnfilteredDispatchable,
EnsureOriginEqualOrHigherPrivilege, EnsureOriginWithArg, MapSuccess, NeverEnsureOrigin,
OriginTrait, TryMapSuccess, UnfilteredDispatchable,
};

mod voting;
Expand Down
92 changes: 89 additions & 3 deletions frame/support/src/traits/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
use crate::dispatch::{DispatchResultWithPostInfo, Parameter, RawOrigin};
use codec::MaxEncodedLen;
use sp_runtime::{
traits::{BadOrigin, Member, Morph, TryMorph},
traits::{BadOrigin, Get, Member, Morph, TryMorph},
Either,
};
use sp_std::marker::PhantomData;
use sp_std::{cmp::Ordering, marker::PhantomData};

use super::misc;

/// Some sort of check on the origin is performed by this object.
pub trait EnsureOrigin<OuterOrigin> {
Expand Down Expand Up @@ -59,7 +61,7 @@ pub trait EnsureOrigin<OuterOrigin> {
}
}

/// `EnsureOrigin` implementation that always fails.
/// [`EnsureOrigin`] implementation that always fails.
pub struct NeverEnsureOrigin<Success>(sp_std::marker::PhantomData<Success>);
impl<OO, Success> EnsureOrigin<OO> for NeverEnsureOrigin<Success> {
type Success = Success;
Expand All @@ -72,6 +74,90 @@ impl<OO, Success> EnsureOrigin<OO> for NeverEnsureOrigin<Success> {
}
}

/// [`EnsureOrigin`] implementation that checks that an origin has equal or higher privilege
/// compared to the expected `Origin`.
///
/// It will take the shortcut of comparing the incoming origin with the expected `Origin` and if
/// both are the same the origin is accepted.
///
/// # Example
///
/// ```rust
/// # use frame_support::traits::{EnsureOriginEqualOrHigherPrivilege, PrivilegeCmp, EnsureOrigin as _};
/// # use sp_runtime::traits::{parameter_types, Get};
/// # use sp_std::cmp::Ordering;
///
/// #[derive(Eq, PartialEq, Debug)]
/// pub enum Origin {
/// Root,
/// SomethingBelowRoot,
/// NormalUser,
/// }
///
/// struct OriginPrivilegeCmp;
///
/// impl PrivilegeCmp<Origin> for OriginPrivilegeCmp {
/// fn cmp_privilege(left: &Origin, right: &Origin) -> Option<Ordering> {
/// match (left, right) {
/// (Origin::Root, Origin::Root) => Some(Ordering::Equal),
/// (Origin::Root, _) => Some(Ordering::Greater),
/// (Origin::SomethingBelowRoot, Origin::SomethingBelowRoot) => Some(Ordering::Equal),
/// (Origin::SomethingBelowRoot, Origin::Root) => Some(Ordering::Less),
/// (Origin::SomethingBelowRoot, Origin::NormalUser) => Some(Ordering::Greater),
/// (Origin::NormalUser, Origin::NormalUser) => Some(Ordering::Equal),
/// (Origin::NormalUser, _) => Some(Ordering::Less),
/// }
/// }
/// }
///
/// parameter_types! {
/// pub const ExpectedOrigin: Origin = Origin::SomethingBelowRoot;
/// }
///
/// type EnsureOrigin = EnsureOriginEqualOrHigherPrivilege<ExpectedOrigin, OriginPrivilegeCmp>;
///
/// // `Root` has an higher privilege as our expected origin.
/// assert!(EnsureOrigin::ensure_origin(Origin::Root).is_ok());
/// // `SomethingBelowRoot` is exactly the expected origin.
/// assert!(EnsureOrigin::ensure_origin(Origin::SomethingBelowRoot).is_ok());
/// // The `NormalUser` origin is not allowed.
/// assert!(EnsureOrigin::ensure_origin(Origin::NormalUser).is_err());
/// ```
pub struct EnsureOriginEqualOrHigherPrivilege<Origin, PrivilegeCmp>(
sp_std::marker::PhantomData<(Origin, PrivilegeCmp)>,
);

impl<OuterOrigin, Origin, PrivilegeCmp> EnsureOrigin<OuterOrigin>
for EnsureOriginEqualOrHigherPrivilege<Origin, PrivilegeCmp>
where
Origin: Get<OuterOrigin>,
OuterOrigin: Eq,
PrivilegeCmp: misc::PrivilegeCmp<OuterOrigin>,
{
type Success = ();

fn try_origin(o: OuterOrigin) -> Result<Self::Success, OuterOrigin> {
let expected_origin = Origin::get();

// If this is the expected origin, it has the same privilege.
if o == expected_origin {
return Ok(())
}

let cmp = PrivilegeCmp::cmp_privilege(&o, &expected_origin);

match cmp {
Some(Ordering::Equal) | Some(Ordering::Greater) => Ok(()),
None | Some(Ordering::Less) => Err(o),
}
}

#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin() -> Result<OuterOrigin, ()> {
Ok(Origin::get())
}
}

/// Some sort of check on the origin is performed by this object.
pub trait EnsureOriginWithArg<OuterOrigin, Argument> {
/// A return type.
Expand Down