use std::time::Duration;
use crate::authority::capability::{
CapabilityKind, CapabilitySemantics, Endpoint, ModerationCapability,
OracleConsultations, OracleResultsForCapability, SubstrateScope, UserCapability,
};
use crate::authority::predicate::{DenialReason, IssuancePolicy, PredicateContext};
use crate::authority::subjects::{
ChannelBinding, ManageAudienceSubject, ModerationSubject, ResourceId, ScopeSelector,
};
use crate::oracle::{
AudienceOracleQuery, AudienceState, BlockOracleQuery, BlockState,
};
use crate::sealed;
macro_rules! capability_marker {
(
$marker:ident,
$class:ident,
$results:ident,
kind: $kind:expr,
max_age: $max_age:expr,
name: $name:literal,
subject: $subject:ty,
semantics: $sem:expr,
block: [$($block_q:expr),* $(,)?],
audience: [$($aud_q:expr),* $(,)?],
mute: [$($mute_q:expr),* $(,)?],
results_block: [$($rb_field:ident),* $(,)?],
results_audience: [$($ra_field:ident),* $(,)?],
results_mute: [$($rm_field:ident),* $(,)?],
) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct $marker;
impl sealed::Sealed for $marker {}
impl $class for $marker {
type Subject = $subject;
type OracleResults = $results;
const KIND: CapabilityKind = $kind;
const MAX_AGE: Duration = $max_age;
const NAME: &'static str = $name;
const ORACLE_CONSULTATIONS: OracleConsultations = OracleConsultations {
block: &[$($block_q,)*],
audience: &[$($aud_q,)*],
mute: &[$($mute_q,)*],
};
const SEMANTICS: CapabilitySemantics = $sem;
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct $results {
$(
/// Block-oracle query result.
pub $rb_field: BlockState,
)*
$(
pub $ra_field: AudienceState,
)*
$(
pub $rm_field: crate::oracle::MuteState,
)*
}
impl sealed::Sealed for $results {}
impl OracleResultsForCapability<$marker> for $results {}
};
}
#[allow(clippy::derivable_impls)]
impl Default for BlockState {
fn default() -> Self {
BlockState::None
}
}
#[allow(clippy::derivable_impls)]
impl Default for AudienceState {
fn default() -> Self {
AudienceState::NoAudienceConfigured
}
}
#[allow(clippy::derivable_impls)]
impl Default for crate::oracle::MuteState {
fn default() -> Self {
crate::oracle::MuteState::None
}
}
capability_marker! {
ViewPrivate, UserCapability, ViewPrivateOracleResults,
kind: CapabilityKind::ViewPrivate,
max_age: Duration::from_secs(300),
name: "view-private",
subject: ResourceId,
semantics: CapabilitySemantics::Read,
block: [BlockOracleQuery::RequesterVsResourceOwner],
audience: [AudienceOracleQuery::RequesterAgainstResourceAudience],
mute: [],
results_block: [block_requester_vs_resource_owner],
results_audience: [audience_requester_against_resource_audience],
results_mute: [],
}
impl IssuancePolicy for ViewPrivate {
fn capability_predicate(
_ctx: &PredicateContext<'_>,
_target: &ResourceId,
_oracle_results: &ViewPrivateOracleResults,
) -> Result<(), DenialReason> {
Ok(())
}
}
capability_marker! {
ParticipatePrivate, UserCapability, ParticipatePrivateOracleResults,
kind: CapabilityKind::ParticipatePrivate,
max_age: Duration::from_secs(60),
name: "participate-private",
subject: ResourceId,
semantics: CapabilitySemantics::Write,
block: [
BlockOracleQuery::RequesterVsResourceOwner,
BlockOracleQuery::RequesterVsParentPostOwner,
],
audience: [AudienceOracleQuery::RequesterAgainstResourceAudience],
mute: [],
results_block: [
block_requester_vs_resource_owner,
block_requester_vs_parent_post_owner,
],
results_audience: [audience_requester_against_resource_audience],
results_mute: [],
}
impl IssuancePolicy for ParticipatePrivate {
fn capability_predicate(
_ctx: &PredicateContext<'_>,
_target: &ResourceId,
_oracle_results: &ParticipatePrivateOracleResults,
) -> Result<(), DenialReason> {
Ok(())
}
}
capability_marker! {
EditPrivatePost, UserCapability, EditPrivatePostOracleResults,
kind: CapabilityKind::EditPrivatePost,
max_age: Duration::from_secs(60),
name: "edit-private-post",
subject: ResourceId,
semantics: CapabilitySemantics::Write,
block: [
BlockOracleQuery::RequesterVsResourceOwner,
BlockOracleQuery::RequesterVsParentPostOwner,
],
audience: [AudienceOracleQuery::RequesterAgainstResourceAudience],
mute: [],
results_block: [
block_requester_vs_resource_owner,
block_requester_vs_parent_post_owner,
],
results_audience: [audience_requester_against_resource_audience],
results_mute: [],
}
impl IssuancePolicy for EditPrivatePost {
fn capability_predicate(
_ctx: &PredicateContext<'_>,
_target: &ResourceId,
_oracle_results: &EditPrivatePostOracleResults,
) -> Result<(), DenialReason> {
Ok(())
}
}
capability_marker! {
DeletePrivatePost, UserCapability, DeletePrivatePostOracleResults,
kind: CapabilityKind::DeletePrivatePost,
max_age: Duration::from_secs(30),
name: "delete-private-post",
subject: ResourceId,
semantics: CapabilitySemantics::Write,
block: [BlockOracleQuery::RequesterVsResourceOwner],
audience: [],
mute: [],
results_block: [block_requester_vs_resource_owner],
results_audience: [],
results_mute: [],
}
impl IssuancePolicy for DeletePrivatePost {
fn capability_predicate(
_ctx: &PredicateContext<'_>,
_target: &ResourceId,
_oracle_results: &DeletePrivatePostOracleResults,
) -> Result<(), DenialReason> {
Ok(())
}
}
capability_marker! {
ManageAudience, UserCapability, ManageAudienceOracleResults,
kind: CapabilityKind::ManageAudience,
max_age: Duration::from_secs(60),
name: "manage-audience",
subject: ManageAudienceSubject,
semantics: CapabilitySemantics::Write,
block: [BlockOracleQuery::RequesterVsResourceOwner],
audience: [],
mute: [],
results_block: [block_requester_vs_resource_owner],
results_audience: [],
results_mute: [],
}
impl IssuancePolicy for ManageAudience {
fn capability_predicate(
_ctx: &PredicateContext<'_>,
_target: &ManageAudienceSubject,
_oracle_results: &ManageAudienceOracleResults,
) -> Result<(), DenialReason> {
Ok(())
}
}
macro_rules! channel_marker {
($marker:ident, $kind:expr, $max_age:expr, $name:literal $(,)?) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct $marker;
impl sealed::Sealed for $marker {}
impl Endpoint for $marker {
type Subject = ChannelBinding;
const KIND: CapabilityKind = $kind;
const MAX_AGE: Duration = $max_age;
const NAME: &'static str = $name;
}
};
}
channel_marker!(EmitToSyncChannel, CapabilityKind::EmitToSyncChannel, Duration::from_secs(60), "emit-to-sync-channel");
channel_marker!(AppViewSync, CapabilityKind::AppViewSync, Duration::from_secs(60), "appview-sync");
channel_marker!(GraphSync, CapabilityKind::GraphSync, Duration::from_secs(60), "graph-sync");
macro_rules! substrate_marker {
($marker:ident, $kind:expr, $max_age:expr, $name:literal $(,)?) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct $marker;
impl sealed::Sealed for $marker {}
impl SubstrateScope for $marker {
type Subject = ScopeSelector;
const KIND: CapabilityKind = $kind;
const MAX_AGE: Duration = $max_age;
const NAME: &'static str = $name;
}
};
}
substrate_marker!(ScanShard, CapabilityKind::ScanShard, Duration::from_secs(120), "scan-shard");
substrate_marker!(ReplicatePrivate, CapabilityKind::ReplicatePrivate, Duration::from_secs(120), "replicate-private");
substrate_marker!(GarbageCollect, CapabilityKind::GarbageCollect, Duration::from_secs(120), "garbage-collect");
macro_rules! moderation_marker {
($marker:ident, $kind:expr, $max_age:expr, $name:literal $(,)?) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct $marker;
impl sealed::Sealed for $marker {}
impl ModerationCapability for $marker {
type Subject = ModerationSubject;
const KIND: CapabilityKind = $kind;
const MAX_AGE: Duration = $max_age;
const NAME: &'static str = $name;
}
};
}
moderation_marker!(ModeratorRead, CapabilityKind::ModeratorRead, Duration::from_secs(30), "moderator-read");
moderation_marker!(ModeratorTakedown, CapabilityKind::ModeratorTakedown, Duration::from_secs(10), "moderator-takedown");
moderation_marker!(ModeratorRestore, CapabilityKind::ModeratorRestore, Duration::from_secs(30), "moderator-restore");
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn view_private_constants_match_spec() {
assert_eq!(ViewPrivate::MAX_AGE, Duration::from_secs(300));
assert_eq!(ViewPrivate::KIND, CapabilityKind::ViewPrivate);
assert_eq!(ViewPrivate::SEMANTICS, CapabilitySemantics::Read);
assert_eq!(ViewPrivate::NAME, "view-private");
assert_eq!(ViewPrivate::ORACLE_CONSULTATIONS.block.len(), 1);
assert_eq!(ViewPrivate::ORACLE_CONSULTATIONS.audience.len(), 1);
assert_eq!(ViewPrivate::ORACLE_CONSULTATIONS.mute.len(), 0);
}
#[test]
fn participate_private_max_age_per_spec() {
assert_eq!(ParticipatePrivate::MAX_AGE, Duration::from_secs(60));
}
#[test]
fn delete_private_post_max_age_per_spec() {
assert_eq!(DeletePrivatePost::MAX_AGE, Duration::from_secs(30));
}
#[test]
fn moderator_takedown_short_max_age_per_spec() {
assert_eq!(ModeratorTakedown::MAX_AGE, Duration::from_secs(10));
}
#[test]
fn edit_private_post_is_write_semantics() {
assert_eq!(EditPrivatePost::SEMANTICS, CapabilitySemantics::Write);
}
#[test]
fn view_private_is_read_semantics() {
assert_eq!(ViewPrivate::SEMANTICS, CapabilitySemantics::Read);
}
}