andromeda_std/ado_contract/
permissioning.rs

1use crate::{
2    ado_base::permissioning::{Permission, PermissionInfo, PermissioningMessage},
3    amp::{messages::AMPPkt, AndrAddr},
4    common::{context::ExecuteContext, OrderBy},
5    error::ContractError,
6};
7use cosmwasm_std::{ensure, Deps, Env, MessageInfo, Order, Response, Storage};
8use cw_storage_plus::{Bound, Index, IndexList, IndexedMap, MultiIndex};
9
10use super::ADOContract;
11
12const MAX_QUERY_LIMIT: u32 = 50;
13const DEFAULT_QUERY_LIMIT: u32 = 25;
14
15pub struct PermissionsIndices<'a> {
16    /// PK: action + actor
17    ///
18    /// Secondary key: actor
19    pub actor: MultiIndex<'a, String, PermissionInfo, String>,
20    pub action: MultiIndex<'a, String, PermissionInfo, String>,
21}
22
23impl<'a> IndexList<PermissionInfo> for PermissionsIndices<'a> {
24    fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<PermissionInfo>> + '_> {
25        let v: Vec<&dyn Index<PermissionInfo>> = vec![&self.action, &self.actor];
26        Box::new(v.into_iter())
27    }
28}
29
30/// Permissions for the ADO contract
31///
32/// Permissions are stored in a multi-indexed map with the primary key being the action and actor
33pub fn permissions<'a>() -> IndexedMap<'a, &'a str, PermissionInfo, PermissionsIndices<'a>> {
34    let indexes = PermissionsIndices {
35        actor: MultiIndex::new(|_pk: &[u8], r| r.actor.clone(), "andr_permissions", "actor"),
36        action: MultiIndex::new(
37            |_pk: &[u8], r| r.action.clone(),
38            "andr_permissions",
39            "action",
40        ),
41    };
42    IndexedMap::new("andr_permissions", indexes)
43}
44
45impl<'a> ADOContract<'a> {
46    pub fn execute_permissioning(
47        &self,
48        ctx: ExecuteContext,
49        msg: PermissioningMessage,
50    ) -> Result<Response, ContractError> {
51        match msg {
52            PermissioningMessage::SetPermission {
53                actor,
54                action,
55                permission,
56            } => self.execute_set_permission(ctx, actor, action, permission),
57            PermissioningMessage::RemovePermission { action, actor } => {
58                self.execute_remove_permission(ctx, actor, action)
59            }
60            PermissioningMessage::PermissionAction { action } => {
61                self.execute_permission_action(ctx, action)
62            }
63            PermissioningMessage::DisableActionPermissioning { action } => {
64                self.execute_disable_action_permission(ctx, action)
65            }
66        }
67    }
68    /// Determines if the provided actor is authorised to perform the given action
69    ///
70    /// Returns an error if the given action is not permissioned for the given actor
71    pub fn is_permissioned(
72        &self,
73        store: &mut dyn Storage,
74        env: Env,
75        action: impl Into<String>,
76        actor: impl Into<String>,
77    ) -> Result<(), ContractError> {
78        // Converted to strings for cloning
79        let action_string: String = action.into();
80        let actor_string: String = actor.into();
81
82        if self.is_contract_owner(store, actor_string.as_str())? {
83            return Ok(());
84        }
85
86        let permission = Self::get_permission(store, action_string.clone(), actor_string.clone())?;
87        let permissioned_action = self
88            .permissioned_actions
89            .may_load(store, action_string.clone())?
90            .unwrap_or(false);
91        match permission {
92            Some(mut permission) => {
93                ensure!(
94                    permission.is_permissioned(&env, permissioned_action),
95                    ContractError::Unauthorized {}
96                );
97
98                // Consume a use for a limited permission
99                if let Permission::Limited { .. } = permission {
100                    permission.consume_use()?;
101                    permissions().save(
102                        store,
103                        (action_string.clone() + actor_string.as_str()).as_str(),
104                        &PermissionInfo {
105                            action: action_string,
106                            actor: actor_string,
107                            permission,
108                        },
109                    )?;
110                }
111
112                Ok(())
113            }
114            None => {
115                ensure!(!permissioned_action, ContractError::Unauthorized {});
116                Ok(())
117            }
118        }
119    }
120
121    /// Determines if the provided actor is authorised to perform the given action
122    ///
123    /// **Ignores the `PERMISSIONED_ACTIONS` map**
124    ///
125    /// Returns an error if the permission has expired or if no permission exists for a restricted ADO
126    pub fn is_permissioned_strict(
127        &self,
128        store: &mut dyn Storage,
129        env: Env,
130        action: impl Into<String>,
131        actor: impl Into<String>,
132    ) -> Result<(), ContractError> {
133        // Converted to strings for cloning
134        let action_string: String = action.into();
135        let actor_string: String = actor.into();
136
137        if self.is_contract_owner(store, actor_string.as_str())? {
138            return Ok(());
139        }
140
141        let permission = Self::get_permission(store, action_string.clone(), actor_string.clone())?;
142        match permission {
143            Some(mut permission) => {
144                ensure!(
145                    permission.is_permissioned(&env, true),
146                    ContractError::Unauthorized {}
147                );
148
149                // Consume a use for a limited permission
150                if let Permission::Limited { .. } = permission {
151                    permission.consume_use()?;
152                    permissions().save(
153                        store,
154                        (action_string.clone() + actor_string.as_str()).as_str(),
155                        &PermissionInfo {
156                            action: action_string,
157                            actor: actor_string,
158                            permission,
159                        },
160                    )?;
161                }
162
163                Ok(())
164            }
165            None => Err(ContractError::Unauthorized {}),
166        }
167    }
168
169    /// Gets the permission for the given action and actor
170    pub fn get_permission(
171        store: &dyn Storage,
172        action: impl Into<String>,
173        actor: impl Into<String>,
174    ) -> Result<Option<Permission>, ContractError> {
175        let action = action.into();
176        let actor = actor.into();
177        let key = action + &actor;
178        if let Some(PermissionInfo { permission, .. }) = permissions().may_load(store, &key)? {
179            Ok(Some(permission))
180        } else {
181            Ok(None)
182        }
183    }
184
185    /// Sets the permission for the given action and actor
186    pub fn set_permission(
187        store: &mut dyn Storage,
188        action: impl Into<String>,
189        actor: impl Into<String>,
190        permission: Permission,
191    ) -> Result<(), ContractError> {
192        let action = action.into();
193        let actor = actor.into();
194        let key = action.clone() + &actor;
195        permissions().save(
196            store,
197            &key,
198            &PermissionInfo {
199                action,
200                actor,
201                permission,
202            },
203        )?;
204        Ok(())
205    }
206
207    /// Removes the permission for the given action and actor
208    pub fn remove_permission(
209        store: &mut dyn Storage,
210        action: impl Into<String>,
211        actor: impl Into<String>,
212    ) -> Result<(), ContractError> {
213        let action = action.into();
214        let actor = actor.into();
215        let key = action + &actor;
216        permissions().remove(store, &key)?;
217        Ok(())
218    }
219
220    /// Execute handler for setting permission
221    ///
222    /// **Whitelisted/Limited permissions will only work for permissioned actions**
223    ///
224    pub fn execute_set_permission(
225        &self,
226        ctx: ExecuteContext,
227        actor: AndrAddr,
228        action: impl Into<String>,
229        permission: Permission,
230    ) -> Result<Response, ContractError> {
231        ensure!(
232            Self::is_contract_owner(self, ctx.deps.storage, ctx.info.sender.as_str())?,
233            ContractError::Unauthorized {}
234        );
235        let actor_addr = actor.get_raw_address(&ctx.deps.as_ref())?;
236        let action = action.into();
237        Self::set_permission(
238            ctx.deps.storage,
239            action.clone(),
240            actor_addr.clone(),
241            permission.clone(),
242        )?;
243
244        Ok(Response::default().add_attributes(vec![
245            ("action", "set_permission"),
246            ("actor", actor_addr.as_str()),
247            ("action", action.as_str()),
248            ("permission", permission.to_string().as_str()),
249        ]))
250    }
251
252    /// Execute handler for setting permission
253    pub fn execute_remove_permission(
254        &self,
255        ctx: ExecuteContext,
256        actor: AndrAddr,
257        action: impl Into<String>,
258    ) -> Result<Response, ContractError> {
259        ensure!(
260            Self::is_contract_owner(self, ctx.deps.storage, ctx.info.sender.as_str())?,
261            ContractError::Unauthorized {}
262        );
263        let actor_addr = actor.get_raw_address(&ctx.deps.as_ref())?;
264        let action = action.into();
265        Self::remove_permission(ctx.deps.storage, action.clone(), actor_addr.clone())?;
266
267        Ok(Response::default().add_attributes(vec![
268            ("action", "remove_permission"),
269            ("actor", actor_addr.as_str()),
270            ("action", action.as_str()),
271        ]))
272    }
273
274    /// Enables permissioning for a given action
275    pub fn permission_action(
276        &self,
277        action: impl Into<String>,
278        store: &mut dyn Storage,
279    ) -> Result<(), ContractError> {
280        self.permissioned_actions
281            .save(store, action.into(), &true)?;
282        Ok(())
283    }
284
285    /// Disables permissioning for a given action
286    pub fn disable_action_permission(&self, action: impl Into<String>, store: &mut dyn Storage) {
287        self.permissioned_actions.remove(store, action.into());
288    }
289
290    pub fn execute_permission_action(
291        &self,
292        ctx: ExecuteContext,
293        action: impl Into<String>,
294    ) -> Result<Response, ContractError> {
295        let action_string: String = action.into();
296        ensure!(
297            Self::is_contract_owner(self, ctx.deps.storage, ctx.info.sender.as_str())?,
298            ContractError::Unauthorized {}
299        );
300        self.permission_action(action_string.clone(), ctx.deps.storage)?;
301        Ok(Response::default().add_attributes(vec![
302            ("action", "permission_action"),
303            ("action", action_string.as_str()),
304        ]))
305    }
306
307    pub fn execute_disable_action_permission(
308        &self,
309        ctx: ExecuteContext,
310        action: impl Into<String>,
311    ) -> Result<Response, ContractError> {
312        let action_string: String = action.into();
313        ensure!(
314            Self::is_contract_owner(self, ctx.deps.storage, ctx.info.sender.as_str())?,
315            ContractError::Unauthorized {}
316        );
317        Self::disable_action_permission(self, action_string.clone(), ctx.deps.storage);
318        Ok(Response::default().add_attributes(vec![
319            ("action", "disable_action_permission"),
320            ("action", action_string.as_str()),
321        ]))
322    }
323
324    /// Queries all permissions for a given actor
325    pub fn query_permissions(
326        &self,
327        deps: Deps,
328        actor: impl Into<String>,
329        limit: Option<u32>,
330        start_after: Option<String>,
331    ) -> Result<Vec<PermissionInfo>, ContractError> {
332        let actor = actor.into();
333        let min = start_after.map(Bound::inclusive);
334        let limit = limit.unwrap_or(DEFAULT_QUERY_LIMIT).min(MAX_QUERY_LIMIT) as usize;
335        let permissions = permissions()
336            .idx
337            .actor
338            .prefix(actor)
339            .range(deps.storage, min, None, Order::Ascending)
340            .take(limit)
341            .map(|p| p.unwrap().1)
342            .collect::<Vec<PermissionInfo>>();
343        Ok(permissions)
344    }
345
346    pub fn query_permissioned_actions(&self, deps: Deps) -> Result<Vec<String>, ContractError> {
347        let actions = self
348            .permissioned_actions
349            .keys(deps.storage, None, None, Order::Ascending)
350            .map(|p| p.unwrap())
351            .collect::<Vec<String>>();
352        Ok(actions)
353    }
354
355    pub fn query_permissioned_actors(
356        &self,
357        deps: Deps,
358        action: impl Into<String>,
359        start_after: Option<String>,
360        limit: Option<u32>,
361        order_by: Option<OrderBy>,
362    ) -> Result<Vec<String>, ContractError> {
363        let action_string: String = action.into();
364        let order_by = match order_by {
365            Some(OrderBy::Desc) => Order::Descending,
366            _ => Order::Ascending,
367        };
368
369        let actors = permissions()
370            .idx
371            .action
372            .prefix(action_string.clone())
373            .keys(
374                deps.storage,
375                start_after.map(Bound::inclusive),
376                None,
377                order_by,
378            )
379            .take((limit).unwrap_or(DEFAULT_QUERY_LIMIT).min(MAX_QUERY_LIMIT) as usize)
380            .map(|p| {
381                p.unwrap()
382                    .strip_prefix(action_string.as_str())
383                    .unwrap()
384                    .to_string()
385            })
386            .collect::<Vec<String>>();
387
388        Ok(actors)
389    }
390}
391
392/// Checks if the provided context is authorised to perform the provided action.
393///
394/// Two scenarios exist:
395/// - The context does not contain any AMP context and the **sender** is the actor
396/// - The context contains AMP context and the **previous sender** or **origin** are considered the actor
397pub fn is_context_permissioned(
398    storage: &mut dyn Storage,
399    info: &MessageInfo,
400    env: &Env,
401    ctx: &Option<AMPPkt>,
402    action: impl Into<String>,
403) -> Result<bool, ContractError> {
404    let contract = ADOContract::default();
405
406    match ctx {
407        Some(amp_ctx) => {
408            let action: String = action.into();
409            let is_origin_permissioned = contract.is_permissioned(
410                storage,
411                env.clone(),
412                action.clone(),
413                amp_ctx.ctx.get_origin().as_str(),
414            );
415            let is_previous_sender_permissioned = contract.is_permissioned(
416                storage,
417                env.clone(),
418                action,
419                amp_ctx.ctx.get_previous_sender().as_str(),
420            );
421            Ok(is_origin_permissioned.is_ok() || is_previous_sender_permissioned.is_ok())
422        }
423        None => Ok(contract
424            .is_permissioned(storage, env.clone(), action, info.sender.to_string())
425            .is_ok()),
426    }
427}
428
429/// Checks if the provided context is authorised to perform the provided action ignoring `PERMISSIONED_ACTIONS`
430///
431/// Two scenarios exist:
432/// - The context does not contain any AMP context and the **sender** is the actor
433/// - The context contains AMP context and the **previous sender** or **origin** are considered the actor
434pub fn is_context_permissioned_strict(
435    storage: &mut dyn Storage,
436    info: &MessageInfo,
437    env: &Env,
438    ctx: &Option<AMPPkt>,
439    action: impl Into<String>,
440) -> Result<bool, ContractError> {
441    let contract = ADOContract::default();
442
443    match ctx {
444        Some(amp_ctx) => {
445            let action: String = action.into();
446            let is_origin_permissioned = contract.is_permissioned_strict(
447                storage,
448                env.clone(),
449                action.clone(),
450                amp_ctx.ctx.get_origin().as_str(),
451            );
452            let is_previous_sender_permissioned = contract.is_permissioned_strict(
453                storage,
454                env.clone(),
455                action,
456                amp_ctx.ctx.get_previous_sender().as_str(),
457            );
458            Ok(is_origin_permissioned.is_ok() || is_previous_sender_permissioned.is_ok())
459        }
460        None => Ok(contract
461            .is_permissioned_strict(storage, env.clone(), action, info.sender.to_string())
462            .is_ok()),
463    }
464}
465
466#[cfg(test)]
467mod tests {
468    use cosmwasm_std::{
469        testing::{mock_dependencies, mock_env, mock_info},
470        Addr,
471    };
472
473    use crate::{ado_base::AndromedaMsg, amp::messages::AMPPkt, common::MillisecondsExpiration};
474
475    use super::*;
476
477    #[test]
478    fn test_permissioned_action() {
479        let mut deps = mock_dependencies();
480        let env = mock_env();
481        let action = "action";
482        let actor = "actor";
483        let contract = ADOContract::default();
484        contract
485            .owner
486            .save(deps.as_mut().storage, &Addr::unchecked("owner"))
487            .unwrap();
488
489        ADOContract::default()
490            .permission_action(action, deps.as_mut().storage)
491            .unwrap();
492
493        // Test Whitelisting
494        let res = contract.is_permissioned(deps.as_mut().storage, env.clone(), action, actor);
495
496        assert!(res.is_err());
497        let permission = Permission::whitelisted(None);
498        ADOContract::set_permission(deps.as_mut().storage, action, actor, permission).unwrap();
499
500        let res = contract.is_permissioned(deps.as_mut().storage, env.clone(), action, actor);
501
502        assert!(res.is_ok());
503
504        ADOContract::remove_permission(deps.as_mut().storage, action, actor).unwrap();
505
506        // Test Limited
507        let res = contract.is_permissioned(deps.as_mut().storage, env.clone(), action, actor);
508
509        assert!(res.is_err());
510        let permission = Permission::limited(None, 1);
511        ADOContract::set_permission(deps.as_mut().storage, action, actor, permission).unwrap();
512
513        let res = contract.is_permissioned(deps.as_mut().storage, env.clone(), action, actor);
514
515        assert!(res.is_ok());
516
517        // Ensure use is consumed
518        let res = contract.is_permissioned(deps.as_mut().storage, env.clone(), action, actor);
519        assert!(res.is_err());
520
521        ADOContract::remove_permission(deps.as_mut().storage, action, actor).unwrap();
522
523        // Test Blacklisted
524        let permission = Permission::blacklisted(None);
525        ADOContract::set_permission(deps.as_mut().storage, action, actor, permission).unwrap();
526
527        let res = contract.is_permissioned(deps.as_mut().storage, env, action, actor);
528
529        assert!(res.is_err());
530    }
531
532    #[test]
533    fn test_unpermissioned_action_blacklisted() {
534        let mut deps = mock_dependencies();
535        let env = mock_env();
536        let action = "action";
537        let actor = "actor";
538        let contract = ADOContract::default();
539        contract
540            .owner
541            .save(deps.as_mut().storage, &Addr::unchecked("owner"))
542            .unwrap();
543
544        ADOContract::default()
545            .permission_action(action, deps.as_mut().storage)
546            .unwrap();
547
548        // Test Blacklisted
549        let permission = Permission::blacklisted(None);
550        ADOContract::set_permission(deps.as_mut().storage, action, actor, permission).unwrap();
551
552        let res = contract.is_permissioned(deps.as_mut().storage, env, action, actor);
553
554        assert!(res.is_err());
555    }
556
557    #[test]
558    fn test_strict_permissioning() {
559        let mut deps = mock_dependencies();
560        let env = mock_env();
561        let action = "action";
562        let actor = "actor";
563        let contract = ADOContract::default();
564        contract
565            .owner
566            .save(deps.as_mut().storage, &Addr::unchecked("owner"))
567            .unwrap();
568
569        let res =
570            contract.is_permissioned_strict(deps.as_mut().storage, env.clone(), action, actor);
571        assert!(res.is_err());
572
573        let permission = Permission::whitelisted(None);
574        ADOContract::set_permission(deps.as_mut().storage, action, actor, permission).unwrap();
575
576        let res = contract.is_permissioned_strict(deps.as_mut().storage, env, action, actor);
577        assert!(res.is_ok());
578    }
579
580    #[test]
581    fn test_owner_escape_clause() {
582        let mut deps = mock_dependencies();
583        let env = mock_env();
584        let action = "action";
585        let actor = "actor";
586        let contract = ADOContract::default();
587        contract
588            .owner
589            .save(deps.as_mut().storage, &Addr::unchecked(actor))
590            .unwrap();
591
592        let res =
593            contract.is_permissioned_strict(deps.as_mut().storage, env.clone(), action, actor);
594        assert!(res.is_ok());
595
596        let res = contract.is_permissioned(deps.as_mut().storage, env, action, actor);
597        assert!(res.is_ok());
598    }
599
600    #[test]
601    fn test_set_permission_unauthorized() {
602        let mut deps = mock_dependencies();
603        let env = mock_env();
604        let contract = ADOContract::default();
605        contract
606            .owner
607            .save(deps.as_mut().storage, &Addr::unchecked("owner"))
608            .unwrap();
609        let msg = AndromedaMsg::Permissioning(PermissioningMessage::SetPermission {
610            actor: AndrAddr::from_string("actor"),
611            action: "action".to_string(),
612            permission: Permission::Whitelisted(None),
613        });
614        let ctx = ExecuteContext::new(deps.as_mut(), mock_info("attacker", &[]), env);
615        let res = contract.execute(ctx, msg);
616
617        assert!(res.is_err());
618        assert_eq!(res.unwrap_err(), ContractError::Unauthorized {});
619    }
620
621    #[test]
622    fn test_permission_action_unauthorized() {
623        let mut deps = mock_dependencies();
624        let env = mock_env();
625        let contract = ADOContract::default();
626        contract
627            .owner
628            .save(deps.as_mut().storage, &Addr::unchecked("owner"))
629            .unwrap();
630        let msg = AndromedaMsg::Permissioning(PermissioningMessage::PermissionAction {
631            action: "action".to_string(),
632        });
633        let ctx = ExecuteContext::new(deps.as_mut(), mock_info("attacker", &[]), env);
634        let res = contract.execute(ctx, msg);
635
636        assert!(res.is_err());
637        assert_eq!(res.unwrap_err(), ContractError::Unauthorized {});
638    }
639
640    #[test]
641    fn test_disable_permissioning_unauthorized() {
642        let mut deps = mock_dependencies();
643        let env = mock_env();
644        let contract = ADOContract::default();
645        contract
646            .owner
647            .save(deps.as_mut().storage, &Addr::unchecked("owner"))
648            .unwrap();
649        let msg = AndromedaMsg::Permissioning(PermissioningMessage::DisableActionPermissioning {
650            action: "action".to_string(),
651        });
652        let ctx = ExecuteContext::new(deps.as_mut(), mock_info("attacker", &[]), env);
653        let res = contract.execute(ctx, msg);
654
655        assert!(res.is_err());
656        assert_eq!(res.unwrap_err(), ContractError::Unauthorized {});
657    }
658
659    #[test]
660    fn test_remove_permission_unauthorized() {
661        let mut deps = mock_dependencies();
662        let env = mock_env();
663        let contract = ADOContract::default();
664        contract
665            .owner
666            .save(deps.as_mut().storage, &Addr::unchecked("owner"))
667            .unwrap();
668        let msg = AndromedaMsg::Permissioning(PermissioningMessage::RemovePermission {
669            action: "action".to_string(),
670            actor: AndrAddr::from_string("actor"),
671        });
672        let ctx = ExecuteContext::new(deps.as_mut(), mock_info("attacker", &[]), env);
673        let res = contract.execute(ctx, msg);
674
675        assert!(res.is_err());
676        assert_eq!(res.unwrap_err(), ContractError::Unauthorized {});
677    }
678
679    #[test]
680    fn test_permission_expiration() {
681        let mut deps = mock_dependencies();
682        let mut env = mock_env();
683        env.block.height = 0;
684        let action = "action";
685        let actor = "actor";
686        let contract = ADOContract::default();
687        let time = 2;
688        let expiration = MillisecondsExpiration::from_seconds(time);
689
690        env.block.time = MillisecondsExpiration::from_seconds(0).into();
691        contract
692            .owner
693            .save(deps.as_mut().storage, &Addr::unchecked("owner"))
694            .unwrap();
695
696        ADOContract::default()
697            .permission_action(action, deps.as_mut().storage)
698            .unwrap();
699
700        let res = contract.is_permissioned(deps.as_mut().storage, env.clone(), action, actor);
701
702        assert!(res.is_err());
703
704        // Test Whitelist
705        let permission = Permission::Whitelisted(Some(expiration));
706        ADOContract::set_permission(deps.as_mut().storage, action, actor, permission).unwrap();
707
708        let res = contract.is_permissioned(deps.as_mut().storage, env.clone(), action, actor);
709        assert!(res.is_ok());
710
711        env.block.time = MillisecondsExpiration::from_seconds(time + 1).into();
712
713        let res = contract.is_permissioned(deps.as_mut().storage, env.clone(), action, actor);
714        assert!(res.is_err());
715
716        env.block.time = MillisecondsExpiration::from_seconds(0).into();
717        // Test Blacklist
718        let permission = Permission::Blacklisted(Some(expiration));
719        ADOContract::set_permission(deps.as_mut().storage, action, actor, permission).unwrap();
720
721        let res = contract.is_permissioned(deps.as_mut().storage, env.clone(), action, actor);
722        assert!(res.is_err());
723
724        env.block.time = MillisecondsExpiration::from_seconds(time + 1).into();
725
726        let res = contract.is_permissioned(deps.as_mut().storage, env, action, actor);
727        assert!(res.is_ok());
728    }
729
730    #[test]
731    fn test_context_permissions() {
732        let mut deps = mock_dependencies();
733        let env = mock_env();
734        let actor = "actor";
735        let info = mock_info(actor, &[]);
736        let action = "action";
737
738        let context = ExecuteContext::new(deps.as_mut(), info.clone(), env.clone());
739        let contract = ADOContract::default();
740
741        contract
742            .owner
743            .save(context.deps.storage, &Addr::unchecked("owner"))
744            .unwrap();
745
746        assert!(is_context_permissioned(
747            context.deps.storage,
748            &context.info,
749            &context.env,
750            &context.amp_ctx,
751            action
752        )
753        .unwrap());
754
755        let context = ExecuteContext::new(deps.as_mut(), info.clone(), env.clone());
756        ADOContract::default()
757            .permission_action(action, context.deps.storage)
758            .unwrap();
759
760        assert!(!is_context_permissioned(
761            context.deps.storage,
762            &context.info,
763            &context.env,
764            &context.amp_ctx,
765            action
766        )
767        .unwrap());
768
769        let context = ExecuteContext::new(deps.as_mut(), info, env.clone());
770        let permission = Permission::whitelisted(None);
771        ADOContract::set_permission(context.deps.storage, action, actor, permission).unwrap();
772
773        assert!(is_context_permissioned(
774            context.deps.storage,
775            &context.info,
776            &context.env,
777            &context.amp_ctx,
778            action
779        )
780        .unwrap());
781
782        let unauth_info = mock_info("mock_actor", &[]);
783        let context = ExecuteContext::new(deps.as_mut(), unauth_info.clone(), env.clone());
784
785        assert!(!is_context_permissioned(
786            context.deps.storage,
787            &context.info,
788            &context.env,
789            &context.amp_ctx,
790            action
791        )
792        .unwrap());
793
794        let amp_ctx = AMPPkt::new("mock_actor", actor, vec![]);
795        let context =
796            ExecuteContext::new(deps.as_mut(), unauth_info.clone(), env.clone()).with_ctx(amp_ctx);
797
798        assert!(is_context_permissioned(
799            context.deps.storage,
800            &context.info,
801            &context.env,
802            &context.amp_ctx,
803            action
804        )
805        .unwrap());
806
807        let amp_ctx = AMPPkt::new(actor, "mock_actor", vec![]);
808        let context =
809            ExecuteContext::new(deps.as_mut(), unauth_info.clone(), env.clone()).with_ctx(amp_ctx);
810
811        assert!(is_context_permissioned(
812            context.deps.storage,
813            &context.info,
814            &context.env,
815            &context.amp_ctx,
816            action
817        )
818        .unwrap());
819
820        let amp_ctx = AMPPkt::new("mock_actor", "mock_actor", vec![]);
821        let context =
822            ExecuteContext::new(deps.as_mut(), unauth_info.clone(), env.clone()).with_ctx(amp_ctx);
823
824        assert!(!is_context_permissioned(
825            context.deps.storage,
826            &context.info,
827            &context.env,
828            &context.amp_ctx,
829            action
830        )
831        .unwrap());
832
833        let amp_ctx = AMPPkt::new("owner", "mock_actor", vec![]);
834        let context =
835            ExecuteContext::new(deps.as_mut(), unauth_info.clone(), env.clone()).with_ctx(amp_ctx);
836
837        assert!(is_context_permissioned(
838            context.deps.storage,
839            &context.info,
840            &context.env,
841            &context.amp_ctx,
842            action
843        )
844        .unwrap());
845
846        let amp_ctx = AMPPkt::new("mock_actor", "owner", vec![]);
847        let context = ExecuteContext::new(deps.as_mut(), unauth_info, env).with_ctx(amp_ctx);
848
849        assert!(is_context_permissioned(
850            context.deps.storage,
851            &context.info,
852            &context.env,
853            &context.amp_ctx,
854            action
855        )
856        .unwrap());
857    }
858
859    #[test]
860    fn test_context_permissions_strict() {
861        let mut deps = mock_dependencies();
862        let env = mock_env();
863        let actor = "actor";
864        let info = mock_info(actor, &[]);
865        let action = "action";
866
867        let context = ExecuteContext::new(deps.as_mut(), info.clone(), env.clone());
868        let contract = ADOContract::default();
869
870        contract
871            .owner
872            .save(context.deps.storage, &Addr::unchecked("owner"))
873            .unwrap();
874
875        assert!(!is_context_permissioned_strict(
876            context.deps.storage,
877            &context.info,
878            &context.env,
879            &context.amp_ctx,
880            action
881        )
882        .unwrap());
883
884        let context = ExecuteContext::new(deps.as_mut(), info, env.clone());
885        let permission = Permission::whitelisted(None);
886        ADOContract::set_permission(context.deps.storage, action, actor, permission).unwrap();
887
888        assert!(is_context_permissioned_strict(
889            context.deps.storage,
890            &context.info,
891            &context.env,
892            &context.amp_ctx,
893            action
894        )
895        .unwrap());
896
897        let unauth_info = mock_info("mock_actor", &[]);
898        let context = ExecuteContext::new(deps.as_mut(), unauth_info.clone(), env.clone());
899
900        assert!(!is_context_permissioned_strict(
901            context.deps.storage,
902            &context.info,
903            &context.env,
904            &context.amp_ctx,
905            action
906        )
907        .unwrap());
908
909        let amp_ctx = AMPPkt::new("mock_actor", actor, vec![]);
910        let context =
911            ExecuteContext::new(deps.as_mut(), unauth_info.clone(), env.clone()).with_ctx(amp_ctx);
912
913        assert!(is_context_permissioned_strict(
914            context.deps.storage,
915            &context.info,
916            &context.env,
917            &context.amp_ctx,
918            action
919        )
920        .unwrap());
921
922        let amp_ctx = AMPPkt::new(actor, "mock_actor", vec![]);
923        let context =
924            ExecuteContext::new(deps.as_mut(), unauth_info.clone(), env.clone()).with_ctx(amp_ctx);
925
926        assert!(is_context_permissioned_strict(
927            context.deps.storage,
928            &context.info,
929            &context.env,
930            &context.amp_ctx,
931            action
932        )
933        .unwrap());
934
935        let amp_ctx = AMPPkt::new("mock_actor", "mock_actor", vec![]);
936        let context = ExecuteContext::new(deps.as_mut(), unauth_info, env).with_ctx(amp_ctx);
937
938        assert!(!is_context_permissioned_strict(
939            context.deps.storage,
940            &context.info,
941            &context.env,
942            &context.amp_ctx,
943            action
944        )
945        .unwrap());
946    }
947
948    #[test]
949    fn test_query_permissions() {
950        let actor = "actor";
951        let mut deps = mock_dependencies();
952
953        let permissions = ADOContract::default()
954            .query_permissions(deps.as_ref(), actor, None, None)
955            .unwrap();
956
957        assert!(permissions.is_empty());
958
959        let permission = Permission::whitelisted(None);
960        let action = "action";
961
962        ADOContract::set_permission(deps.as_mut().storage, action, actor, permission.clone())
963            .unwrap();
964
965        let permissions = ADOContract::default()
966            .query_permissions(deps.as_ref(), actor, None, None)
967            .unwrap();
968
969        assert_eq!(permissions.len(), 1);
970        assert_eq!(permissions[0].action, action);
971        assert_eq!(permissions[0].permission, permission);
972
973        let multi_permissions = vec![
974            ("action2".to_string(), Permission::blacklisted(None)),
975            ("action3".to_string(), Permission::whitelisted(None)),
976            ("action4".to_string(), Permission::blacklisted(None)),
977            ("action5".to_string(), Permission::whitelisted(None)),
978        ];
979
980        for (action, permission) in multi_permissions {
981            ADOContract::set_permission(deps.as_mut().storage, &action, actor, permission).unwrap();
982        }
983
984        let permissions = ADOContract::default()
985            .query_permissions(deps.as_ref(), actor, None, None)
986            .unwrap();
987
988        assert_eq!(permissions.len(), 5);
989    }
990
991    #[test]
992    fn test_query_permissioned_actions() {
993        let mut deps = mock_dependencies();
994        let env = mock_env();
995        let info = mock_info("owner", &[]);
996        let ctx = ExecuteContext {
997            deps: deps.as_mut(),
998            env,
999            info: info.clone(),
1000            amp_ctx: None,
1001        };
1002
1003        let contract = ADOContract::default();
1004
1005        contract.owner.save(ctx.deps.storage, &info.sender).unwrap();
1006
1007        let actions = ADOContract::default()
1008            .query_permissioned_actions(ctx.deps.as_ref())
1009            .unwrap();
1010
1011        assert!(actions.is_empty());
1012
1013        ADOContract::default()
1014            .execute_permission_action(ctx, "action")
1015            .unwrap();
1016
1017        let actions = ADOContract::default()
1018            .query_permissioned_actions(deps.as_ref())
1019            .unwrap();
1020
1021        assert_eq!(actions.len(), 1);
1022        assert_eq!(actions[0], "action");
1023    }
1024
1025    #[test]
1026    fn test_query_permissioned_actors() {
1027        let mut deps = mock_dependencies();
1028        let env = mock_env();
1029        let info = mock_info("owner", &[]);
1030        let ctx = ExecuteContext {
1031            deps: deps.as_mut(),
1032            env,
1033            info: info.clone(),
1034            amp_ctx: None,
1035        };
1036
1037        let contract = ADOContract::default();
1038
1039        contract.owner.save(ctx.deps.storage, &info.sender).unwrap();
1040
1041        let actor = "actor";
1042        let action = "action";
1043        ADOContract::default()
1044            .execute_permission_action(ctx, action)
1045            .unwrap();
1046
1047        ADOContract::set_permission(deps.as_mut().storage, action, actor, Permission::default())
1048            .unwrap();
1049        let actors = ADOContract::default()
1050            .query_permissioned_actors(deps.as_ref(), action, None, None, None)
1051            .unwrap();
1052
1053        assert_eq!(actors.len(), 1);
1054        assert_eq!(actors[0], actor);
1055    }
1056}