Skip to content

Commit 6664bd3

Browse files
authored
feat: add admin role management functions and corresponding tests (#296)
* feat: add admin role management functions and corresponding tests * fix fmt
1 parent 42c4dd7 commit 6664bd3

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

contract/contracts/access-control/src/lib.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,70 @@ impl AccessControl {
215215

216216
Ok(())
217217
}
218+
219+
/// Checks if a user is the current super admin.
220+
///
221+
/// # Arguments
222+
/// * `user` - The address to check.
223+
///
224+
/// # Returns
225+
/// `true` if the user is the current super admin, `false` otherwise.
226+
pub fn is_admin(env: Env, user: Address) -> bool {
227+
match Self::get_admin(env) {
228+
Ok(admin) => admin == user,
229+
Err(_) => false,
230+
}
231+
}
232+
233+
/// Revokes all roles from a user.
234+
///
235+
/// Only the current super admin can call this function.
236+
///
237+
/// # Arguments
238+
/// * `admin_caller` - The address of the admin calling the function.
239+
/// * `user` - The address from which all roles will be revoked.
240+
///
241+
/// # Errors
242+
/// * `Unauthorized` - If the caller is not the super admin.
243+
pub fn revoke_all_roles(env: Env, admin_caller: Address, user: Address) -> Result<(), Error> {
244+
admin_caller.require_auth();
245+
246+
let current_admin = Self::get_admin(env.clone())?;
247+
if admin_caller != current_admin {
248+
return Err(Error::Unauthorized);
249+
}
250+
251+
// Revoke all possible roles
252+
for role in [Role::Admin, Role::Operator, Role::Moderator].iter() {
253+
let key = DataKey::Role(user.clone(), role.clone());
254+
if env.storage().persistent().has(&key) {
255+
env.storage().persistent().remove(&key);
256+
}
257+
}
258+
259+
Ok(())
260+
}
261+
262+
/// Checks if a user has any of the specified roles.
263+
///
264+
/// # Arguments
265+
/// * `user` - The address to check.
266+
/// * `roles` - A vector of roles to check.
267+
///
268+
/// # Returns
269+
/// `true` if the user has at least one of the specified roles, `false` otherwise.
270+
pub fn has_any_role(env: Env, user: Address, roles: soroban_sdk::Vec<Role>) -> bool {
271+
for role in roles.iter() {
272+
if env
273+
.storage()
274+
.persistent()
275+
.has(&DataKey::Role(user.clone(), role))
276+
{
277+
return true;
278+
}
279+
}
280+
false
281+
}
218282
}
219283

220284
mod test;

contract/contracts/access-control/src/test.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,113 @@ fn test_unauthorized_assignment() {
125125
let result = client.try_assign_role(&non_admin, &user, &Role::Operator);
126126
assert_eq!(result, Err(Ok(Error::Unauthorized)));
127127
}
128+
#[test]
129+
fn test_is_admin() {
130+
let env = Env::default();
131+
env.mock_all_auths();
132+
133+
let contract_id = env.register(AccessControl, ());
134+
let client = AccessControlClient::new(&env, &contract_id);
135+
136+
let admin = Address::generate(&env);
137+
let non_admin = Address::generate(&env);
138+
139+
client.init(&admin);
140+
141+
// Check that admin is recognized as admin
142+
assert!(client.is_admin(&admin));
143+
144+
// Check that non-admin is not admin
145+
assert!(!client.is_admin(&non_admin));
146+
}
147+
148+
#[test]
149+
fn test_revoke_all_roles() {
150+
let env = Env::default();
151+
env.mock_all_auths();
152+
153+
let contract_id = env.register(AccessControl, ());
154+
let client = AccessControlClient::new(&env, &contract_id);
155+
156+
let admin = Address::generate(&env);
157+
let user = Address::generate(&env);
158+
159+
client.init(&admin);
160+
161+
// Assign multiple roles to user
162+
client.assign_role(&admin, &user, &Role::Operator);
163+
client.assign_role(&admin, &user, &Role::Moderator);
164+
165+
assert!(client.has_role(&user, &Role::Operator));
166+
assert!(client.has_role(&user, &Role::Moderator));
167+
168+
// Revoke all roles at once
169+
client.revoke_all_roles(&admin, &user);
170+
171+
// Verify all roles are removed
172+
assert!(!client.has_role(&user, &Role::Operator));
173+
assert!(!client.has_role(&user, &Role::Moderator));
174+
assert!(!client.has_role(&user, &Role::Admin));
175+
}
176+
177+
#[test]
178+
fn test_revoke_all_roles_unauthorized() {
179+
let env = Env::default();
180+
env.mock_all_auths();
181+
182+
let contract_id = env.register(AccessControl, ());
183+
let client = AccessControlClient::new(&env, &contract_id);
184+
185+
let admin = Address::generate(&env);
186+
let non_admin = Address::generate(&env);
187+
let user = Address::generate(&env);
188+
189+
client.init(&admin);
190+
client.assign_role(&admin, &user, &Role::Operator);
191+
192+
// Non-admin tries to revoke all roles
193+
let result = client.try_revoke_all_roles(&non_admin, &user);
194+
assert_eq!(result, Err(Ok(Error::Unauthorized)));
195+
}
196+
197+
#[test]
198+
fn test_has_any_role() {
199+
let env = Env::default();
200+
env.mock_all_auths();
201+
202+
let contract_id = env.register(AccessControl, ());
203+
let client = AccessControlClient::new(&env, &contract_id);
204+
205+
let admin = Address::generate(&env);
206+
let user = Address::generate(&env);
207+
208+
client.init(&admin);
209+
210+
// User has no roles yet
211+
let mut empty_roles = soroban_sdk::Vec::new(&env);
212+
empty_roles.push_back(Role::Operator);
213+
empty_roles.push_back(Role::Moderator);
214+
assert!(!client.has_any_role(&user, &empty_roles));
215+
216+
// Assign Operator role to user
217+
client.assign_role(&admin, &user, &Role::Operator);
218+
219+
// Now user should have any role (Operator is in the list)
220+
assert!(client.has_any_role(&user, &empty_roles));
221+
222+
// Create a list with roles user doesn't have
223+
let mut other_roles = soroban_sdk::Vec::new(&env);
224+
other_roles.push_back(Role::Admin);
225+
other_roles.push_back(Role::Moderator);
226+
227+
// User still has Operator (not in this list), so should return false
228+
assert!(!client.has_any_role(&user, &other_roles));
229+
230+
// Add Admin to the list - still false because user is not admin
231+
let mut mixed_roles = soroban_sdk::Vec::new(&env);
232+
mixed_roles.push_back(Role::Operator);
233+
mixed_roles.push_back(Role::Admin);
234+
235+
// Now should be true because user has Operator
236+
assert!(client.has_any_role(&user, &mixed_roles));
237+
}

0 commit comments

Comments
 (0)