smart_account_auth/messages/
impls.rs

1use 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
26// a list e.g. Vec of Impl FromStr
27impl<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