smart_account_auth/messages/
impls.rs1use std::fmt::Display;
2use strum::IntoDiscriminant;
3use saa_common::{AuthError, SessionError, FromStr, ToString, ensure};
4use super::actions::{Action, ActionDerivation, AllowedActions, DerivableMsg};
5
6
7
8impl core::fmt::Display for Action {
9 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
10 write!(f, "{}", self.result)
11 }
12}
13
14
15impl FromStr for Action {
16 type Err = AuthError;
17 fn from_str(s: &str) -> Result<Self, Self::Err> {
18 Ok(Action {
19 result: s.to_string(),
20 method: ActionDerivation::Name
21 })
22 }
23}
24
25
26impl<A : ToString> From<Vec<A>> for AllowedActions {
28 fn from(actions: Vec<A>) -> Self {
29 if actions.is_empty() {
30 return AllowedActions::All {};
31 } else {
32 AllowedActions::Include(actions.into_iter()
33 .map(|action| {
34 let result = action.to_string();
35 Action {
36 result,
37 method: ActionDerivation::Name
38 }
39 })
40 .collect())
41 }
42 }
43}
44
45
46
47
48
49#[cfg(feature = "wasm")]
50impl<M> DerivableMsg for M
51where
52 M : IntoDiscriminant + Display + serde::Serialize + Clone,
53 <M as IntoDiscriminant>::Discriminant : ToString + AsRef<str>,
54{
55 fn name(&self) -> String {
56 self.discriminant().to_string()
57 }
58
59 fn to_json_string(&self) -> Result<String, AuthError> {
60 saa_common::to_json_string(self)
61 .map_err(|_| AuthError::generic("Failed to convert to JSON string"))
62 }
63}
64
65
66
67#[cfg(not(feature = "wasm"))]
68impl<M> DerivableMsg for M
69where
70 M : IntoDiscriminant<Discriminant : ToString + AsRef<str>> + Display + Clone
71{
72 fn name(&self) -> String {
73 self.discriminant().to_string()
74 }
75}
76
77
78
79
80fn is_session_action(name: &str) -> bool {
81 name.is_empty() || name.contains("session_actions") || name.contains("session_info")
82}
83
84
85
86
87
88impl Action {
89
90 #[cfg(not(feature = "wasm"))]
91 pub fn new<M : DerivableMsg>(message: &M, method: ActionDerivation) -> Result<Self, SessionError> {
92 let name = message.discriminant().to_string();
93 ensure!(!is_session_action(name.as_str()), SessionError::InnerSessionAction);
94 let action = match method {
95 ActionDerivation::Name => Self {
96 method: ActionDerivation::Name,
97 result: message.discriminant().to_string(),
98 },
99 ActionDerivation::String => Self {
100 method: ActionDerivation::String,
101 result: message.to_string(),
102 },
103 };
104 ensure!(!action.result.is_empty(), SessionError::InvalidActions);
105 Ok(action)
106 }
107
108 #[cfg(feature = "wasm")]
109 pub fn new<M : DerivableMsg>(message: &M, method: ActionDerivation) -> Result<Self, SessionError> {
110
111 let name = message.discriminant().to_string();
112 ensure!(!is_session_action(name.as_str()), SessionError::InnerSessionAction);
113 let action = match method {
114 ActionDerivation::Name => Self {
115 method: ActionDerivation::Name,
116 result: message.discriminant().to_string(),
117 },
118 ActionDerivation::String => Self {
119 method: ActionDerivation::String,
120 result: message.to_string(),
121 },
122 ActionDerivation::Json => Self {
123 method: ActionDerivation::Json,
124 result: saa_common::to_json_string(message)
125 .map_err(|_| SessionError::DerivationError)?,
126 },
127 };
128 ensure!(!action.result.is_empty(), SessionError::InvalidActions);
129 Ok(action)
130
131 }
132
133 #[cfg(feature = "utils")]
134 pub fn with_str<A : core::fmt::Display>(message: A) -> Self {
135 Self {
136 method: ActionDerivation::String,
137 result: message.to_string()
138 }
139 }
140
141 #[cfg(feature = "utils")]
142 pub fn with_strum_name<A>(message: A) -> Self
143 where A: IntoDiscriminant<Discriminant : ToString>,
144 {
145 Self {
146 method: ActionDerivation::Name,
147 result: message.discriminant().to_string()
148 }
149 }
150
151 #[cfg(all(feature = "wasm", feature = "utils"))]
152 pub fn with_serde_name<A : serde::Serialize>(message: A) -> Result<Self, SessionError> {
153 Ok(Self {
154 method: ActionDerivation::Name,
155 result: serde_json::to_value(message)
156 .map_err(|_| SessionError::DerivationError)?
157 .as_object()
158 .map(|obj| obj.keys()
159 .next()
160 .map(|k| k.to_string())
161 )
162 .flatten()
163 .ok_or(SessionError::DerivationError)?
164 })
165 }
166
167 #[cfg(all(feature = "wasm", feature = "utils"))]
168 pub fn with_serde_json<A : serde::Serialize>(message: A) -> Result<Self, SessionError> {
169 Ok(Self {
170 method: ActionDerivation::Json,
171 result: saa_common::to_json_string(&message)
172 .map_err(|_| SessionError::DerivationError)?
173 })
174
175 }
176}
177
178
179impl AllowedActions {
180
181 pub fn can_do_action(&self, act: &Action) -> bool {
182 if is_session_action(act.result.as_str()) {
183 return false;
184 }
185 match self {
186 AllowedActions::All {} => true,
187 AllowedActions::Include(ref actions) => actions
188 .iter()
189 .any(|action| action == act)
190 }
191 }
192
193 pub fn can_do_msg<M : DerivableMsg>(&self, message: &M) -> bool {
194 if is_session_action(message.name().as_str()) {
195 return false;
196 }
197 match self {
198 AllowedActions::All {} => true,
199 AllowedActions::Include(ref actions) => actions
200 .iter()
201 .any(|allowed|
202 if let Ok(derived) = Action::new(message, allowed.method.clone()) {
203 allowed.result == derived.result
204 } else {
205 false
206 }
207 )
208 }
209 }
210}
211
212
213
214
215
216
217
218
219#[cfg(feature = "utils")]
220impl AllowedActions {
221
222
223 pub fn can_do_str<S: saa_common::ToString>(&self, msg: &S) -> bool {
224 match self {
225 AllowedActions::All {} => true,
226 AllowedActions::Include(ref actions) => actions
227 .iter()
228 .any(|action|
229 action.method == ActionDerivation::String &&
230 action.result == msg.to_string()
231 )
232 }
233 }
234
235 #[cfg(feature = "wasm")]
236 pub fn can_do_json<M : serde::Serialize>(&self, msg: &M) -> bool {
237 match self {
238 AllowedActions::All {} => true,
239 AllowedActions::Include(ref actions) => actions
240 .iter()
241 .any(|action| {
242 if action.method != ActionDerivation::Json {
243 return false;
244 }
245 let res = Action::with_serde_json(msg)
246 .map(|msg| msg.result)
247 .unwrap_or_default();
248 action.result == res
249 })
250 }
251
252 }
253
254}
255
256
257
258
259
260
261
262
263
264#[cfg(all(feature = "wasm", feature = "session"))]
265impl super::sessions::SessionInfo {
266 pub fn checked_params(
267 &self,
268 env: &saa_common::wasm::Env,
269 actions: Option<&AllowedActions>
270 ) -> Result<(crate::CredentialId, crate::CredentialRecord, crate::Expiration, AllowedActions), SessionError> {
271 use saa_common::ensure;
272 let granter = self.granter.clone().unwrap_or_default();
273 let (id, info) = self.grantee.clone();
274 ensure!(!id.is_empty(), SessionError::InvalidGrantee);
275 let expiration = self.expiration.clone().unwrap_or_default();
276 ensure!(!expiration.is_expired(&env.block), SessionError::Expired);
277 if let Some(granter) = &self.granter {
278 ensure!(!granter.is_empty() && *granter != id, SessionError::InvalidGranter);
279 }
280 let actions : AllowedActions = match actions {
281 Some(actions) => {
282 if let AllowedActions::Include(ref actions) = actions {
283 ensure!(actions.len() > 0, SessionError::EmptyCreateActions);
284 actions
285 .iter()
286 .enumerate()
287 .try_for_each(|(i, action)| {
288 ensure!(
289 !action.result.is_empty() && actions
290 .into_iter()
291 .skip(i + 1)
292 .filter(|action2| action == *action2)
293 .count() == 0,
294 SessionError::InvalidActions
295 );
296 ensure!(
297 !is_session_action(action.result.as_str()),
298 SessionError::InnerSessionAction
299 );
300 Ok(())
301 })?;
302 }
303 actions.clone()
304 },
305 None => AllowedActions::All {},
306 };
307 Ok((granter, (id, info), expiration, actions))
308 }
309}
310
311