Skip to content

Commit 112aec9

Browse files
authored
feat(access-manager): special case internal calls (#5222)
2 parents 8a81a51 + 5712f41 commit 112aec9

File tree

7 files changed

+91
-16
lines changed

7 files changed

+91
-16
lines changed

cosmwasm/access-manager/src/contract.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ pub(crate) fn set_target_function_role<'a>(
344344
only_authorized(ctx)?;
345345

346346
for selector in selectors {
347-
_set_target_function_role(ctx, target, selector, role_id);
347+
_set_target_function_role(ctx, target, selector, role_id)?;
348348
}
349349

350350
Ok(())
@@ -358,14 +358,23 @@ fn _set_target_function_role(
358358
target: &Addr,
359359
selector: &Selector,
360360
role_id: RoleId,
361-
) {
361+
) -> Result<(), ContractError> {
362+
if selector.is_internal() {
363+
return Err(ContractError::AccessManager(
364+
AccessManagerError::InternalSelector(selector.to_owned()),
365+
));
366+
}
367+
362368
ctx.storage()
363369
.write::<TargetAllowedRoles>(&(target.clone(), selector.to_owned()), &role_id);
370+
364371
ctx.emit(TargetFunctionRoleUpdated {
365372
target,
366373
selector,
367374
role_id,
368375
});
376+
377+
Ok(())
369378
}
370379

371380
/// See [`ExecuteMsg::SetTargetAdminDelay`].
@@ -986,7 +995,12 @@ pub(crate) fn can_call(
986995
target: &Addr,
987996
selector: &Selector,
988997
) -> Result<CanCall, ContractError> {
989-
if is_target_closed(ctx, target)? {
998+
if selector.is_internal() {
999+
Ok(CanCall {
1000+
allowed: true,
1001+
delay: 0,
1002+
})
1003+
} else if is_target_closed(ctx, target)? {
9901004
Ok(CanCall {
9911005
allowed: false,
9921006
delay: 0,
@@ -1046,6 +1060,12 @@ pub(crate) fn get_target_function_role(
10461060
target: &Addr,
10471061
selector: &Selector,
10481062
) -> Result<RoleId, ContractError> {
1063+
if selector.is_internal() {
1064+
return Err(ContractError::AccessManager(
1065+
AccessManagerError::InternalSelector(selector.to_owned()),
1066+
));
1067+
}
1068+
10491069
Ok(ctx
10501070
.storage()
10511071
.maybe_read::<TargetAllowedRoles>(&(target.clone(), selector.to_owned()))?

cosmwasm/access-manager/src/tests.rs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use unionlabs_primitives::H256;
1515

1616
use crate::{
1717
error::ContractError,
18-
execute, min_setback,
18+
execute, min_setback, query,
1919
tests::utils::{assert_query_result, setup, ACCOUNT_1, ACCOUNT_2, ADMIN, TARGET_1, TARGET_2},
2020
};
2121

@@ -1503,3 +1503,54 @@ fn schedule_reentrant_works() {
15031503
}),
15041504
);
15051505
}
1506+
1507+
#[test]
1508+
fn target_role_internal_selector_fails() {
1509+
let (mut deps, env) = setup();
1510+
1511+
assert_eq!(
1512+
execute(
1513+
deps.as_mut(),
1514+
env.clone(),
1515+
message_info(&ADMIN, &[]).clone(),
1516+
ExecuteMsg::SetTargetFunctionRole {
1517+
selectors: vec![Selector::new("$$internal_method").to_owned()],
1518+
role_id: RoleId::new(1),
1519+
target: TARGET_1.clone(),
1520+
},
1521+
)
1522+
.unwrap_err(),
1523+
ContractError::AccessManager(AccessManagerError::InternalSelector(
1524+
Selector::new("$$internal_method").to_owned()
1525+
))
1526+
);
1527+
1528+
assert_eq!(
1529+
query(
1530+
deps.as_ref(),
1531+
env.clone(),
1532+
QueryMsg::GetTargetFunctionRole {
1533+
target: TARGET_1.clone(),
1534+
selector: Selector::new("$$also_internal").to_owned(),
1535+
},
1536+
)
1537+
.unwrap_err(),
1538+
ContractError::AccessManager(AccessManagerError::InternalSelector(
1539+
Selector::new("$$also_internal").to_owned()
1540+
))
1541+
);
1542+
1543+
assert_query_result(
1544+
deps.as_ref(),
1545+
&env,
1546+
QueryMsg::CanCall {
1547+
selector: Selector::new("$$internal_yet_again").to_owned(),
1548+
target: TARGET_1.clone(),
1549+
caller: ACCOUNT_1.clone(),
1550+
},
1551+
&CanCall {
1552+
allowed: true,
1553+
delay: 0,
1554+
},
1555+
);
1556+
}

cosmwasm/access-manager/src/tests/utils.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ use cosmwasm_std::{
1414
Addr, Deps, Env, OwnedDeps, Response,
1515
};
1616
use depolama::StorageExt;
17+
use frissitheto::UpgradeMsg;
1718
use serde::de::DeserializeOwned;
1819

19-
use crate::{init, query, state::RoleMembers};
20+
use crate::{migrate, query, state::RoleMembers};
2021

2122
pub static ADMIN: LazyLock<Addr> = LazyLock::new(|| Addr::unchecked("admin"));
2223

@@ -30,12 +31,12 @@ pub fn setup() -> (OwnedDeps<MockStorage, MockApi, MockQuerier>, Env) {
3031
let mut deps = mock_dependencies();
3132
let env = mock_env();
3233

33-
let res = init(
34+
let res = migrate(
3435
deps.as_mut(),
35-
&env,
36-
&InitMsg {
36+
env.clone(),
37+
UpgradeMsg::Init(InitMsg {
3738
initial_admin: ADMIN.clone(),
38-
},
39+
}),
3940
)
4041
.unwrap();
4142

e2e/access-managed-example/src/msg.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub enum ExecuteMsg {
1414
by: u32,
1515
in_sub_msg: bool,
1616
},
17+
#[serde(rename = "$$decrement_in_sub_msg")]
1718
DecrementInSubMsg {
1819
by: u32,
1920
},

e2e/access-manager-tests/src/main.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,6 @@ async fn main() -> Result<()> {
251251
info!("set INCREMENT role guardian");
252252
info!("set DECREMENT role guardian");
253253
info!("set INCREMENT_IN_REPLY role guardian");
254-
info!("set DECREMENT_IN_SUB_MSG role guardian");
255254
info!("grant INCREMENT to bob with an execution delay");
256255

257256
execute(
@@ -273,12 +272,6 @@ async fn main() -> Result<()> {
273272
selectors: vec![Selector::new("increment_in_reply").to_owned()],
274273
role_id: INCREMENT_IN_REPLY,
275274
},
276-
// reentrant call, access is checked by the contract itself
277-
&manager::msg::ExecuteMsg::SetTargetFunctionRole {
278-
target: Addr::unchecked(managed.to_string()),
279-
selectors: vec![Selector::new("decrement_in_sub_msg").to_owned()],
280-
role_id: RoleId::PUBLIC_ROLE,
281-
},
282275
&manager::msg::ExecuteMsg::SetRoleGuardian {
283276
role_id: INCREMENT,
284277
guardian: INCREMENT_GUARDIAN,

lib/access-manager-types/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ impl<Context> bincode::Decode<Context> for Box<Selector> {
199199
}
200200

201201
impl Selector {
202+
pub const INTERNAL_PREFIX: &str = "$$";
203+
202204
pub fn new(selector: &str) -> &Self {
203205
unsafe { &*(ptr::from_ref::<str>(selector) as *const Self) }
204206
}
@@ -505,6 +507,10 @@ impl Selector {
505507

506508
t.serialize(ExtractSelector(None)).unwrap()
507509
}
510+
511+
pub fn is_internal(&self) -> bool {
512+
self.0.starts_with(Self::INTERNAL_PREFIX)
513+
}
508514
}
509515

510516
impl fmt::Display for Selector {

lib/access-manager-types/src/manager/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,7 @@ pub enum AccessManagerError {
4949
target: Addr,
5050
selector: Box<Selector>,
5151
},
52+
53+
#[error("cannot set permissions for internal selector {0}")]
54+
InternalSelector(Box<Selector>),
5255
}

0 commit comments

Comments
 (0)