cw_auths/
lib.rs

1mod utils;
2#[cfg(feature = "utils")]
3mod methods;
4#[cfg(feature = "session")]
5mod session;
6
7
8pub use types::{StoredCredentials, UpdateOperation};
9
10#[cfg(feature = "session")]
11pub use {
12    types::{sessions::{queries::*, actions::*}, macros::{session_action, session_query}},
13    session::{handle_session_actions, handle_session_queries},
14};
15#[cfg(feature = "types")]
16pub use {
17    types::stores, smart_account_auth as saa_types
18};
19
20#[cfg(feature = "utils")]
21pub use {methods::*, utils::*};
22
23
24
25use smart_account_auth::{
26    traits::Verifiable,
27    msgs::SignedDataMsg,
28    CredentialData, CredentialRecord, CredentialInfo, CredentialName, CredentialId,
29    ensure
30};
31use types::{
32    stores::{ACCOUNT_NUMBER, HAS_NATIVES, VERIFYING_ID, CREDENTIAL_INFOS as CREDS},
33    wasm::{Api, Env, MessageInfo, Storage},
34    errors::{AuthError, StorageError}, 
35    serde
36};
37
38
39pub fn account_number(
40    storage: &dyn Storage
41) -> u64 {
42    ACCOUNT_NUMBER.load(storage).unwrap_or(0)
43}
44
45
46
47
48pub fn verify_native(
49    storage: &dyn Storage,
50    sender: String
51) -> Result<(), StorageError> {
52    ensure!(CREDS.has(storage, sender), StorageError::NotFound);
53    Ok(())
54}
55
56
57pub fn verify_signed(
58    api: &dyn Api,
59    storage: &dyn Storage,
60    env: &Env,
61    msg: SignedDataMsg
62) -> Result<(), AuthError> {
63    utils::convert_validate(msg.data.as_slice(), env, account_number(storage))?;
64    utils::cred_from_signed(api, storage, msg)?;
65    Ok(())
66} 
67
68
69
70pub fn verify_signed_actions<T : serde::de::DeserializeOwned>(
71    api: &dyn Api,
72    storage: &mut dyn Storage,
73    env: &Env,
74    msg: SignedDataMsg
75) -> Result<Vec<T>, AuthError> {
76    let nonce = account_number(storage);
77    let signed = utils::convert_validate_return(msg.data.as_slice(), env, nonce)?;
78    utils::cred_from_signed(api, storage, msg)?;
79    ACCOUNT_NUMBER.save(storage, &(nonce + 1))?;
80    Ok(signed.messages)
81}
82
83
84
85
86
87pub fn has_natives(
88    storage: &dyn Storage
89) -> bool {
90    HAS_NATIVES.load(storage).unwrap_or(false)
91}
92
93
94
95pub fn get_stored_credentials(
96    storage: &dyn Storage
97) -> Result<StoredCredentials, StorageError> {
98
99
100    Ok(StoredCredentials { 
101        has_natives: has_natives(storage),
102        verifying_id: VERIFYING_ID.load(storage).map_err(|_| StorageError::NotFound)?,
103        records: utils::get_credential_records(storage)?,
104        account_number: account_number(storage), 
105        #[cfg(feature = "session")]
106        sessions    :   None,
107    })
108}
109
110
111
112
113pub fn save_credentials(
114    api: &dyn Api,
115    storage: &mut dyn Storage,
116    env: &Env,
117    info: &MessageInfo,
118    data: &CredentialData
119) -> Result<CredentialData, AuthError> {
120    let data = data.with_native(info);
121    data.validate()?;
122    data.checked_replay( env, 0u64)?;
123
124    ACCOUNT_NUMBER.save(storage, &1u64)?;
125
126    let mut has_natives = false;
127    for cred in data.credentials.iter() {
128        cred.verify_cosmwasm(api)?;
129        let info = cred.info();
130        if info.name == CredentialName::Native { 
131            has_natives = true 
132        }
133        CREDS.save(storage, cred.id(), &info)?;
134    }
135    HAS_NATIVES.save(storage, &has_natives)?;
136
137    let verifying = match data.primary_index {
138        Some(i) => data.credentials[i as usize].id(),
139        None => data.credentials.first().unwrap().id()
140    };
141    VERIFYING_ID.save(storage, &verifying)?;
142    Ok(data)
143}
144
145
146
147
148
149
150
151pub fn reset_credentials(
152    storage: &mut dyn Storage,
153    acc_number: bool,
154    #[cfg(feature = "session")]
155    sessions: bool
156) -> Result<(), StorageError> {
157    VERIFYING_ID.remove(storage);
158    HAS_NATIVES.remove(storage);
159    CREDS.clear(storage);
160    if acc_number {
161        ACCOUNT_NUMBER.remove(storage);
162    }
163    #[cfg(feature = "session")]
164    {
165        if sessions {
166            types::stores::SESSIONS.clear(storage);
167        }
168    }
169    Ok(())
170}
171
172
173
174
175/* pub fn update_credentials_signed(
176    api: &dyn Api,
177    storage: &mut dyn Storage,
178    env: &Env,
179    info: &MessageInfo,
180    msg: SignedDataMsg
181) -> Result<(), AuthError> {
182
183    let nonce = account_number(storage);
184    let signed : MsgDataToSign<UpdateOperation> = convert_validate_return(
185        msg.data.as_slice(), env, nonce
186    )?;
187    let cred = cred_from_signed(api, storage, msg)?;
188
189    for op in signed.messages {
190        let had_natives = HAS_NATIVES.load(storage)?;
191        match op {
192            UpdateOperation::Add(data) => {
193                data.with_credential(cred.clone()).validate_replay_all(storage, env)?;
194                add_credentials(api, storage, data.with_native(info.sender.as_str()), had_natives)?;
195            },
196            UpdateOperation::Remove(idx) => {
197                remove_credentials(storage, idx, had_natives)?;
198            }
199        }
200    }
201    ACCOUNT_NUMBER.save(storage, &(nonce + 1))?;
202    Ok(())
203}
204 */
205
206
207
208pub fn update_credentials(
209    api: &dyn Api,
210    storage: &mut dyn Storage,
211    op: UpdateOperation,
212) -> Result<(), AuthError> {
213    let had_natives = HAS_NATIVES.load(storage)?;
214    match op {
215        UpdateOperation::Add(data) => {
216            add_credentials(api, storage, data, had_natives)
217        },
218        UpdateOperation::Remove(idx) => {
219            remove_credentials(storage, idx, had_natives)?;
220            Ok(())
221        }
222    }
223}
224
225
226
227
228
229pub fn add_credentials(
230    api: &dyn Api,
231    storage: &mut dyn Storage,
232    data: CredentialData,
233    had_natives: bool
234) -> Result<(), AuthError> {
235    
236    data.validate()?;
237    data.verify_cosmwasm(api)?;
238
239    if let Some(ix) = data.primary_index {
240        VERIFYING_ID.save(storage, &data.credentials[ix as usize].id())?;
241    }
242
243    let mut has_natives = had_natives;
244
245    for cred in data.credentials.iter() {
246        let id = cred.id();
247        ensure!(!CREDS.has(storage, id.clone()), StorageError::AlreadyExists);
248        let info = cred.info();
249        if !has_natives && info.name == CredentialName::Native {
250            has_natives = true;
251        }
252        CREDS.save(storage, id, &info)?;
253    }
254
255    if !had_natives && has_natives {
256        HAS_NATIVES.save(storage, &true)?;
257    }   
258
259    if !VERIFYING_ID.exists(storage) {
260        VERIFYING_ID.save(storage, &data.credentials[0].id())?;
261    }
262    Ok(())
263}
264
265
266
267pub fn remove_credentials(
268    storage: &mut dyn Storage,
269    idx: Vec<CredentialId>,
270    had_natives: bool
271) -> Result<Vec<CredentialRecord>, AuthError> {
272    ensure!(!idx.is_empty(), AuthError::generic("Must supply at least one credential to remove"));
273
274    let all_creds = utils::get_credential_records(storage)?;
275    let left = all_creds.len() - idx.len();
276    ensure!(left > 0, AuthError::generic("Must leave at least one credential"));
277
278    let verifying_id = VERIFYING_ID.load(storage)?;
279    let mut native_changed = false;
280    let mut verifying_removed = false;
281
282    let remaining : Vec<(String, CredentialInfo)> = all_creds
283        .into_iter()
284        .filter(|(id, info)| {
285            if idx.contains(id) {
286                if info.name == CredentialName::Native {
287                    native_changed = true;
288                }
289                if *id == verifying_id {
290                    verifying_removed = true;
291                }
292                CREDS.remove(storage, id.to_string());
293                false
294            } else {
295                true
296            }
297        }).collect();
298        
299    if had_natives && native_changed {
300        let still_has = remaining
301            .iter()
302            .any(|(_, info)| info.name == CredentialName::Native);
303        HAS_NATIVES.save(storage, &still_has)?;
304    }
305
306    if verifying_removed {
307        let first = remaining.first().unwrap();
308        VERIFYING_ID.save(storage, &first.0)?;
309    }
310
311    Ok(remaining)
312}
313