lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

permission.rs (5852B)


      1 use crate::error::RadrootsNostrConnectError;
      2 use crate::method::RadrootsNostrConnectMethod;
      3 use serde::{Deserialize, Deserializer, Serialize, Serializer};
      4 use std::fmt;
      5 use std::str::FromStr;
      6 
      7 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
      8 pub struct RadrootsNostrConnectPermission {
      9     pub method: RadrootsNostrConnectMethod,
     10     pub parameter: Option<String>,
     11 }
     12 
     13 impl RadrootsNostrConnectPermission {
     14     pub fn new(method: RadrootsNostrConnectMethod) -> Self {
     15         Self {
     16             method,
     17             parameter: None,
     18         }
     19     }
     20 
     21     pub fn with_parameter(
     22         method: RadrootsNostrConnectMethod,
     23         parameter: impl Into<String>,
     24     ) -> Self {
     25         Self {
     26             method,
     27             parameter: Some(parameter.into()),
     28         }
     29     }
     30 
     31     pub fn matches_request(
     32         &self,
     33         method: &RadrootsNostrConnectMethod,
     34         parameter: Option<&str>,
     35     ) -> bool {
     36         if self.method != *method {
     37             return false;
     38         }
     39         match (&self.method, self.parameter.as_deref(), parameter) {
     40             (RadrootsNostrConnectMethod::SignEvent, None, _) => true,
     41             (RadrootsNostrConnectMethod::SignEvent, Some(configured), Some(requested)) => {
     42                 match (
     43                     sign_event_kind_parameter(configured),
     44                     sign_event_kind_parameter(requested),
     45                 ) {
     46                     (Some(configured), Some(requested)) => configured == requested,
     47                     _ => false,
     48                 }
     49             }
     50             (_, None, None) => true,
     51             (_, Some(configured), Some(requested)) => configured == requested,
     52             _ => false,
     53         }
     54     }
     55 
     56     pub fn matches_sign_event_kind(&self, event_kind: u32) -> bool {
     57         let event_kind = event_kind.to_string();
     58         self.matches_request(
     59             &RadrootsNostrConnectMethod::SignEvent,
     60             Some(event_kind.as_str()),
     61         )
     62     }
     63 }
     64 
     65 impl fmt::Display for RadrootsNostrConnectPermission {
     66     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     67         match self.parameter.as_deref() {
     68             Some(parameter) => write!(f, "{}:{parameter}", self.method),
     69             None => write!(f, "{}", self.method),
     70         }
     71     }
     72 }
     73 
     74 impl FromStr for RadrootsNostrConnectPermission {
     75     type Err = RadrootsNostrConnectError;
     76 
     77     fn from_str(value: &str) -> Result<Self, Self::Err> {
     78         let trimmed = value.trim();
     79         if trimmed.is_empty() {
     80             return Err(RadrootsNostrConnectError::InvalidPermission(
     81                 value.to_owned(),
     82             ));
     83         }
     84 
     85         let (method, parameter) = match trimmed.split_once(':') {
     86             Some((method, parameter)) if !parameter.is_empty() => (method, Some(parameter)),
     87             Some(_) => {
     88                 return Err(RadrootsNostrConnectError::InvalidPermission(
     89                     value.to_owned(),
     90                 ));
     91             }
     92             None => (trimmed, None),
     93         };
     94 
     95         Ok(Self {
     96             method: RadrootsNostrConnectMethod::from_str(method)?,
     97             parameter: parameter.map(ToOwned::to_owned),
     98         })
     99     }
    100 }
    101 
    102 #[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
    103 pub struct RadrootsNostrConnectPermissions(Vec<RadrootsNostrConnectPermission>);
    104 
    105 impl RadrootsNostrConnectPermissions {
    106     pub fn new() -> Self {
    107         Self::default()
    108     }
    109 
    110     pub fn as_slice(&self) -> &[RadrootsNostrConnectPermission] {
    111         self.0.as_slice()
    112     }
    113 
    114     pub fn into_vec(self) -> Vec<RadrootsNostrConnectPermission> {
    115         self.0
    116     }
    117 
    118     pub fn is_empty(&self) -> bool {
    119         self.0.is_empty()
    120     }
    121 
    122     pub fn allows_request(
    123         &self,
    124         method: &RadrootsNostrConnectMethod,
    125         parameter: Option<&str>,
    126     ) -> bool {
    127         self.0
    128             .iter()
    129             .any(|permission| permission.matches_request(method, parameter))
    130     }
    131 
    132     pub fn allows_sign_event_kind(&self, event_kind: u32) -> bool {
    133         self.0
    134             .iter()
    135             .any(|permission| permission.matches_sign_event_kind(event_kind))
    136     }
    137 }
    138 
    139 fn sign_event_kind_parameter(value: &str) -> Option<u32> {
    140     let value = value.strip_prefix("kind:").unwrap_or(value);
    141     if value.is_empty() || !value.chars().all(|character| character.is_ascii_digit()) {
    142         return None;
    143     }
    144     value.parse().ok()
    145 }
    146 
    147 impl From<Vec<RadrootsNostrConnectPermission>> for RadrootsNostrConnectPermissions {
    148     fn from(value: Vec<RadrootsNostrConnectPermission>) -> Self {
    149         Self(value)
    150     }
    151 }
    152 
    153 impl fmt::Display for RadrootsNostrConnectPermissions {
    154     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    155         let rendered = self
    156             .0
    157             .iter()
    158             .map(ToString::to_string)
    159             .collect::<Vec<_>>()
    160             .join(",");
    161         f.write_str(&rendered)
    162     }
    163 }
    164 
    165 impl FromStr for RadrootsNostrConnectPermissions {
    166     type Err = RadrootsNostrConnectError;
    167 
    168     fn from_str(value: &str) -> Result<Self, Self::Err> {
    169         let trimmed = value.trim();
    170         if trimmed.is_empty() {
    171             return Ok(Self::default());
    172         }
    173 
    174         let permissions = trimmed
    175             .split(',')
    176             .map(RadrootsNostrConnectPermission::from_str)
    177             .collect::<Result<Vec<_>, _>>()?;
    178         Ok(Self(permissions))
    179     }
    180 }
    181 
    182 impl Serialize for RadrootsNostrConnectPermissions {
    183     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    184     where
    185         S: Serializer,
    186     {
    187         serializer.serialize_str(&self.to_string())
    188     }
    189 }
    190 
    191 impl<'de> Deserialize<'de> for RadrootsNostrConnectPermissions {
    192     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    193     where
    194         D: Deserializer<'de>,
    195     {
    196         let value = String::deserialize(deserializer)?;
    197         Self::from_str(&value).map_err(serde::de::Error::custom)
    198     }
    199 }