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 }