From 8c6ee4024fe1c1ff2a8a980fc3ff9082781e2253 Mon Sep 17 00:00:00 2001 From: pfed-prog Date: Thu, 2 Oct 2025 17:57:43 +0200 Subject: [PATCH 1/2] closes #217 --- .../functions/list_all_registered_users.rs | 104 +++++++++++++++--- 1 file changed, 88 insertions(+), 16 deletions(-) diff --git a/contracts/user_management/src/functions/list_all_registered_users.rs b/contracts/user_management/src/functions/list_all_registered_users.rs index e46329f..84095b7 100644 --- a/contracts/user_management/src/functions/list_all_registered_users.rs +++ b/contracts/user_management/src/functions/list_all_registered_users.rs @@ -16,7 +16,19 @@ fn string_contains(haystack: &String, needle: &String) -> bool { /// Security constants const MAX_PAGE_SIZE_ABSOLUTE: u32 = 1000; - +/// Lists all registered users with pagination and filtering (admin-only). +/// +/// # Arguments +/// +/// * `env` - Soroban environment. +/// * `caller` - Address of the caller (must be admin). +/// * `page` - The page number for pagination. +/// * `page_size` - The size of each page. +/// * `filter` - Optional filter criteria for users. +/// +/// # Returns +/// +/// * `Vec` - A vector of lightweight user profiles. pub fn list_all_users( env: Env, caller: Address, @@ -81,7 +93,7 @@ pub fn list_all_users( } } - let total_filtered = filtered_profiles.len(); + let total_filtered: u32 = filtered_profiles.len(); if total_filtered == 0 { return Vec::new(&env); } @@ -120,7 +132,15 @@ pub fn list_all_users( result } -/// Checks whether the system is properly initialized +/// Checks whether the system is properly initialized. +/// +/// # Arguments +/// +/// * `env` - Soroban environment. +/// +/// # Returns +/// +/// * `bool` - True if the system is initialized, otherwise false. fn is_system_initialized(env: &Env) -> bool { if let Some(config) = env .storage() @@ -133,7 +153,15 @@ fn is_system_initialized(env: &Env) -> bool { } } -/// Gets the admin configuration with defaults +/// Gets the admin configuration with defaults. +/// +/// # Arguments +/// +/// * `env` - Soroban environment. +/// +/// # Returns +/// +/// * `AdminConfig` - The admin configuration. fn get_admin_config(env: &Env) -> AdminConfig { env.storage() .persistent() @@ -141,7 +169,16 @@ fn get_admin_config(env: &Env) -> AdminConfig { .unwrap_or_else(|| handle_error(env, Error::SystemNotInitialized)) } -/// Checks whether the caller is an admin with enhanced security +/// Checks whether the caller is an admin with enhanced security. +/// +/// # Arguments +/// +/// * `env` - Soroban environment. +/// * `who` - Address of the caller. +/// +/// # Returns +/// +/// * `bool` - True if the caller is an admin, otherwise false. fn is_admin(env: &Env, who: &Address) -> bool { // First check if system is initialized if !is_system_initialized(env) { @@ -166,7 +203,16 @@ fn is_admin(env: &Env, who: &Address) -> bool { } } -/// Checks if a profile matches the given filter criteria +/// Checks if a profile matches the given filter criteria. +/// +/// # Arguments +/// +/// * `profile` - The user profile to check. +/// * `filter` - Optional filter criteria. +/// +/// # Returns +/// +/// * `bool` - True if the profile matches the filter, otherwise false. fn matches_filter( profile: &LightProfile, filter: &Option, @@ -213,7 +259,17 @@ fn matches_filter( true } -/// Validates and sanitizes input parameters +/// Validates and sanitizes input parameters. +/// +/// # Arguments +/// +/// * `page_size` - The size of the page to validate. +/// * `filter` - Optional filter criteria. +/// * `config` - The admin configuration to use for validation. +/// +/// # Returns +/// +/// * `Result<(), &'static str>` - Ok if validation passes, Err with a message otherwise. fn validate_input( page_size: u32, filter: &Option, @@ -279,7 +335,7 @@ pub fn list_all_users_cursor( } // Get admin configuration - let config = get_admin_config(&env); + let config: AdminConfig = get_admin_config(&env); // Authorization: only admins can call if !is_admin(&env, &caller) { @@ -308,17 +364,17 @@ pub fn list_all_users_cursor( } // Find the starting index based on cursor - let start_index = if let Some(cursor) = &pagination.cursor { + let start_index: u32 = if let Some(cursor) = &pagination.cursor { find_address_index(&users_index, cursor).unwrap_or(0) } else { 0 }; // Collect filtered profiles starting from the cursor position - let mut result_data = Vec::new(&env); - let mut processed_count = 0; + let mut result_data: Vec = Vec::new(&env); + let mut processed_count: u32 = 0; let mut next_cursor: Option
= None; - let mut total_matching = 0u32; + let mut total_matching: u32 = 0u32; for i in start_index..users_index.len() { if processed_count >= pagination.limit { @@ -361,11 +417,11 @@ pub fn list_all_users_cursor( } // Determine if there are more pages - let has_more = if next_cursor.is_some() { + let has_more: bool = if next_cursor.is_some() { true } else { // Check if there are more items after the current batch - let mut found_more = false; + let mut found_more: bool = false; for i in (start_index + processed_count)..users_index.len() { if let Some(addr) = users_index.get(i) { if let Some(profile) = env @@ -399,7 +455,14 @@ pub fn list_all_users_cursor( /// Finds the index of an address in the users index vector. /// -/// Returns the index if found, None otherwise. +/// # Arguments +/// +/// * `users_index` - A vector of user addresses. +/// * `target` - The address to find. +/// +/// # Returns +/// +/// * `Option` - The index if found, None otherwise. fn find_address_index(users_index: &Vec
, target: &Address) -> Option { for i in 0..users_index.len() { if let Some(addr) = users_index.get(i) { @@ -412,12 +475,21 @@ fn find_address_index(users_index: &Vec
, target: &Address) -> Option` - Ok if validation passes, Err with a message otherwise. fn validate_pagination_params( pagination: &PaginationParams, config: &AdminConfig, ) -> Result<(), &'static str> { // Validate limit - let max_allowed = config.max_page_size.min(MAX_PAGE_SIZE_ABSOLUTE); + let max_allowed: u32 = config.max_page_size.min(MAX_PAGE_SIZE_ABSOLUTE); if pagination.limit == 0 { return Err("limit must be greater than 0"); } From 2b9e72de3a8de529e78c8b269654d3d75ff7b0e5 Mon Sep 17 00:00:00 2001 From: pfed-prog Date: Thu, 2 Oct 2025 18:00:37 +0200 Subject: [PATCH 2/2] closes #216 --- .../src/functions/get_user_by_id.rs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/contracts/user_management/src/functions/get_user_by_id.rs b/contracts/user_management/src/functions/get_user_by_id.rs index 639ae64..e77997d 100644 --- a/contracts/user_management/src/functions/get_user_by_id.rs +++ b/contracts/user_management/src/functions/get_user_by_id.rs @@ -7,7 +7,21 @@ use crate::error::{handle_error, Error}; use crate::schema::{DataKey, UserProfile}; use core::iter::Iterator; - +/// Retrieves a user profile by user ID. +/// +/// # Arguments +/// +/// * `env` - Soroban environment. +/// * `requester` - The address of the requester (must be authenticated). +/// * `user_id` - The address of the user whose profile is to be retrieved. +/// +/// # Returns +/// +/// * `UserProfile` - The user profile associated with the provided user ID. +/// +/// # Panics +/// +/// * Panics if the requester is not authorized or if the profile does not exist. pub fn get_user_by_id(env: Env, requester: Address, user_id: Address) -> UserProfile { // Require authentication for the requester requester.require_auth(); @@ -28,6 +42,16 @@ pub fn get_user_by_id(env: Env, requester: Address, user_id: Address) -> UserPro profile } +/// Checks whether the given address is an admin. +/// +/// # Arguments +/// +/// * `env` - Soroban environment. +/// * `who` - The address to check for admin status. +/// +/// # Returns +/// +/// * `bool` - True if the address is an admin, otherwise false. fn is_admin(env: &Env, who: &Address) -> bool { // Use the secure admin check from admin_management module use crate::schema::AdminConfig;