Skip to content

feat(identity-registry): implement Role-Based Access Control (RBAC) with Moderators#58

Merged
Bosun-Josh121 merged 1 commit intoLightForgeHub:mainfrom
Josue19-08:feat/rbac-moderators
Mar 28, 2026
Merged

feat(identity-registry): implement Role-Based Access Control (RBAC) with Moderators#58
Bosun-Josh121 merged 1 commit intoLightForgeHub:mainfrom
Josue19-08:feat/rbac-moderators

Conversation

@Josue19-08
Copy link
Copy Markdown
Contributor

@Josue19-08 Josue19-08 commented Mar 28, 2026

🧠 SkillSphere Pull Request 🌐

Mark with an x all the checkboxes that apply (like [x])


📌 Type of Change

  • 📚 Documentation (updates to README, docs, or comments)
  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ Enhancement (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to change)
  • 🏗️ Refactor (code improvement/cleanup without logical changes)

📝 Changes Description

Implements Role-Based Access Control (RBAC) in identity-registry-contract by introducing a Moderator role that separates day-to-day operations from critical admin actions.

Files changed:

  • src/storage.rs: Added DataKey::Moderator(Address) variant and is_moderator, set_moderator, remove_moderator storage helpers using instance storage.
  • src/error.rs: Added Unauthorized = 11 error variant for callers that are neither admin nor moderator.
  • src/contract.rs: Added add_moderator and remove_moderator functions (admin only). Updated verify_expert and ban_expert to accept a caller: &Address parameter and authorize either admin or moderator.
  • src/lib.rs: Exposed add_moderator and remove_moderator in the contract interface. Updated add_expert and ban_expert to include the caller: Address parameter.
  • src/test.rs: Updated all existing tests for new function signatures. Added 4 new tests: moderator verifies expert (success), moderator bans expert (success), non-moderator cannot verify (fail), remove moderator revokes access.

Storage impact: Moderator flags are stored in instance storage as DataKey::Moderator(Address) -> bool, so they share the contract instance TTL. No additional persistent storage entries.


📸 Evidence

running 29 tests
test test::test_ban_before_contract_initialized ... ok
test test::test_batch_update_profiles_max_vec - should panic ... ok
test test::test_add_expert_unauthorized - should panic ... ok
test test::test_batch_verification_no_admin - should panic ... ok
test test::test_batch_verification_max_vec - should panic ... ok
test test::test_ban_unverified_expert ... ok
test test::test_batch_update_profiles_uri_too_long ... ok
test test::test_ban_expert_unauthorized - should panic ... ok
test test::test_add_expert ... ok
test test::test_ban_expert ... ok
test test::test_data_uri_persisted_on_verify ... ok
test test::test_complete_expert_lifecycle ... ok
test test::test_expert_directory_no_duplicates_on_reverify ... ok
test test::test_initialization ... ok
test test::test_expert_status_changed_event ... ok
test test::test_ban_expert_workflow ... ok
test test::test_non_moderator_cannot_verify_expert ... ok
test test::test_getters ... ok
test test::test_expert_directory_via_batch_add ... ok
test test::test_expert_directory_enumeration ... ok
test test::test_batch_verification_check_status ... ok
test test::test_remove_moderator ... ok
test test::test_moderator_can_verify_expert ... ok
test test::test_moderator_can_ban_expert ... ok
test test::test_update_profile_rejections ... ok
test test::test_batch_update_profiles ... ok
test test::test_update_profile_updates_uri_and_emits_event ... ok
test test::test_unban_expert ... ok
test test::test_expert_pagination ... ok

test result: ok. 29 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.19s

🌌 Comments

  • add_expert and ban_expert are breaking changes: callers must now pass a caller: Address as the first argument to explicitly identify who is authorizing the action (admin or moderator).
  • add_moderator and remove_moderator are strictly admin-only — moderators cannot escalate their own privileges.
  • Contract upgrades remain admin-only by design (not touched in this PR).

Thank you for contributing to SkillSphere! 🌍

We are glad you have chosen to help us democratize access to knowledge on the Stellar network. Your contribution brings us one step closer to a trustless, peer-to-peer consulting economy. Let's build the future together! 🚀

Summary by CodeRabbit

  • New Features

    • Introduced moderator role management, enabling administrators to add and remove moderators.
    • Extended expert verification and banning permissions to moderators, in addition to administrators.
  • Tests

    • Added comprehensive test coverage for moderator functionality and authorization scenarios.

Add Moderator role to identity-registry-contract separating day-to-day
operations (verify/ban experts) from critical admin actions.

- storage: add DataKey::Moderator(Address) with is/set/remove helpers
- contract: add add_moderator/remove_moderator (admin only); update
  verify_expert and ban_expert to accept caller param (admin or moderator)
- error: add Unauthorized(11) variant
- lib: expose add_moderator and remove_moderator; update add_expert and
  ban_expert signatures to include caller address
- test: add moderator verify/ban success tests, unauthorized tests,
  and remove_moderator test; update all existing tests for new signatures
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 28, 2026

📝 Walkthrough

Walkthrough

This change introduces role-based access control for the identity registry contract by adding moderator management functions and updating expert verification and banning to allow either admin or moderator authorization. Storage, error types, and test coverage are updated to support the new moderator role.

Changes

Cohort / File(s) Summary
Core Authorization Logic
contracts/identity-registry-contract/src/contract.rs
Added add_moderator() and remove_moderator() (admin-only). Updated verify_expert() and ban_expert() signatures to include caller parameter; changed authorization from admin-only to accept either admin or moderator, with RegistryError::Unauthorized for invalid callers. Event emissions updated to record caller instead of admin.
Error Types
contracts/identity-registry-contract/src/error.rs
Added new enum variant RegistryError::Unauthorized = 11 to represent unauthorized access attempts.
Public Contract Interface
contracts/identity-registry-contract/src/lib.rs
Exposed add_moderator() and remove_moderator() as public contract entrypoints. Updated add_expert() and ban_expert() signatures to include caller parameter; updated documentation to reflect Admin-or-Moderator authorization.
Storage Layer
contracts/identity-registry-contract/src/storage.rs
Added DataKey::Moderator(Address) enum variant. Introduced helper functions is_moderator(), set_moderator(), and remove_moderator() to manage moderator membership in instance storage.
Test Coverage
contracts/identity-registry-contract/src/test.rs
Updated all expert management test calls to include explicit caller parameter. Added test cases validating moderator role permissions: moderator can verify/ban experts, non-moderators receive RegistryError::Unauthorized. Added storage assertions for moderator lifecycle.

Sequence Diagram

sequenceDiagram
    actor Admin
    actor Moderator
    participant Contract
    participant Storage
    participant Result

    Admin->>Contract: verify_expert(caller=Admin, expert, uri)
    Contract->>Contract: admin.require_auth()
    activate Contract
    Contract->>Storage: (proceed with verification)
    deactivate Contract
    Contract->>Result: ✓ Success

    Moderator->>Contract: verify_expert(caller=Moderator, expert, uri)
    Contract->>Storage: is_moderator(Moderator)?
    activate Storage
    Storage->>Storage: Check DataKey::Moderator(Moderator)
    Storage-->>Contract: true
    deactivate Storage
    Contract->>Storage: (proceed with verification)
    Contract->>Result: ✓ Success

    Moderator->>Contract: verify_expert(caller=NotModerator, expert, uri)
    Contract->>Storage: is_moderator(NotModerator)?
    activate Storage
    Storage-->>Contract: false
    deactivate Storage
    Contract->>Result: ✗ RegistryError::Unauthorized
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PR #10: Introduces initial ban_expert() and verify_expert() functions with admin-only authorization; this PR adapts those functions to support moderator access.
  • PR #9: Adds admin-only verify_expert() to the expert verification flow; this PR extends it with moderator authorization and caller parameter.
  • PR #13: Modifies expert verification and authorization logic; shares overlapping changes to verify_expert() and ban_expert() authorization patterns.

Poem

🐰 Hop, hop, the mods arise!
No longer lone admins in the skies,
With DataKeys and storage so bright,
Moderators dance through day and night. 🌙✨
Permission checks spread wide and far,
Each role a gleaming, trusted star.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: implementing Role-Based Access Control with a Moderators feature, which matches the core objective of the changeset.
Description check ✅ Passed The PR description covers all required template sections with concrete details: issue reference (#43), test verification (29 tests passed), type of change selection, detailed changes description with file-by-file breakdown, test evidence, and implementation notes.
Linked Issues check ✅ Passed The PR fully satisfies issue #43 requirements: moderator storage with DataKey::Moderator and helpers [storage.rs], admin-only add/remove_moderator functions [contract.rs], contract interface exposure [lib.rs], and comprehensive test coverage including moderator success/failure cases [test.rs].
Out of Scope Changes check ✅ Passed All changes directly support the RBAC moderator role objective: storage, error variant, contract logic, interface exposure, and test updates are strictly scoped to implementing moderator functionality without extraneous modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
contracts/identity-registry-contract/src/contract.rs (1)

96-108: ⚠️ Potential issue | 🔴 Critical

Reject Banned records in verify_expert.

A moderator can currently call this on a banned expert, which flips them back to Verified and appends the same address to the directory again on Line 108. That bypasses the admin-only unban_expert flow this RBAC split is supposed to preserve. Please block ExpertStatus::Banned here, or route reactivation through unban_expert, and add a regression test for that transition.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/identity-registry-contract/src/contract.rs` around lines 96 - 108,
In verify_expert, add an explicit check for ExpertStatus::Banned and return an
error instead of re-verifying or adding to the index: if current_status ==
ExpertStatus::Banned { return Err(RegistryError::Banned); } (or an appropriate
RegistryError variant) so storage::set_expert_record and
storage::add_expert_to_index are not executed for banned accounts;
alternatively, route reactivation through the admin-only unban_expert path. Also
add a regression test asserting that calling verify_expert on a banned expert
fails and does not change storage or append the address to the index.
🧹 Nitpick comments (2)
contracts/identity-registry-contract/src/lib.rs (1)

47-59: Publish the ABI break with this entrypoint change.

These signatures change the generated contract interface, so existing callers will fail until they pass the new caller argument. Please ship this with an explicit version bump or migration note for downstream clients.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/identity-registry-contract/src/lib.rs` around lines 47 - 59, The
exported entrypoints add_expert and ban_expert now accept an extra caller:
Address parameter (they call contract::verify_expert and contract::ban_expert),
which is an ABI-breaking change; update the contract release metadata by bumping
the contract/ABI version and add a clear migration note describing the new
caller argument and how callers must pass it (or provide backward-compatible
shim entrypoints that derive caller from env and delegate to the new
signatures), and include this change in the contract changelog/release notes so
downstream clients can update accordingly.
contracts/identity-registry-contract/src/contract.rs (1)

88-94: Consider a shared admin/moderator auth helper.

The same authorization branch is duplicated in both entrypoints. Pulling it into one helper will make future RBAC changes much less likely to drift between verify_expert and ban_expert.

Also applies to: 125-131

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/identity-registry-contract/src/contract.rs` around lines 88 - 94,
The authorization branch that checks if caller is admin or a moderator (using
admin, caller.require_auth(), storage::is_moderator(env, caller), and returning
RegistryError::Unauthorized) is duplicated between the entrypoints (e.g.,
verify_expert and ban_expert); extract this into a single helper function (e.g.,
require_admin_or_moderator_auth(env, caller, admin) or
verify_admin_or_moderator) that performs the same checks and calls
require_auth() or returns RegistryError::Unauthorized, then replace the
duplicated branches in both verify_expert and ban_expert with calls to that
helper to centralize RBAC logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@contracts/identity-registry-contract/src/contract.rs`:
- Around line 96-108: In verify_expert, add an explicit check for
ExpertStatus::Banned and return an error instead of re-verifying or adding to
the index: if current_status == ExpertStatus::Banned { return
Err(RegistryError::Banned); } (or an appropriate RegistryError variant) so
storage::set_expert_record and storage::add_expert_to_index are not executed for
banned accounts; alternatively, route reactivation through the admin-only
unban_expert path. Also add a regression test asserting that calling
verify_expert on a banned expert fails and does not change storage or append the
address to the index.

---

Nitpick comments:
In `@contracts/identity-registry-contract/src/contract.rs`:
- Around line 88-94: The authorization branch that checks if caller is admin or
a moderator (using admin, caller.require_auth(), storage::is_moderator(env,
caller), and returning RegistryError::Unauthorized) is duplicated between the
entrypoints (e.g., verify_expert and ban_expert); extract this into a single
helper function (e.g., require_admin_or_moderator_auth(env, caller, admin) or
verify_admin_or_moderator) that performs the same checks and calls
require_auth() or returns RegistryError::Unauthorized, then replace the
duplicated branches in both verify_expert and ban_expert with calls to that
helper to centralize RBAC logic.

In `@contracts/identity-registry-contract/src/lib.rs`:
- Around line 47-59: The exported entrypoints add_expert and ban_expert now
accept an extra caller: Address parameter (they call contract::verify_expert and
contract::ban_expert), which is an ABI-breaking change; update the contract
release metadata by bumping the contract/ABI version and add a clear migration
note describing the new caller argument and how callers must pass it (or provide
backward-compatible shim entrypoints that derive caller from env and delegate to
the new signatures), and include this change in the contract changelog/release
notes so downstream clients can update accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c7f0b164-b826-46e6-9e3f-b51010ff0ee3

📥 Commits

Reviewing files that changed from the base of the PR and between 1a4ddef and c5c82e4.

📒 Files selected for processing (5)
  • contracts/identity-registry-contract/src/contract.rs
  • contracts/identity-registry-contract/src/error.rs
  • contracts/identity-registry-contract/src/lib.rs
  • contracts/identity-registry-contract/src/storage.rs
  • contracts/identity-registry-contract/src/test.rs

@Bosun-Josh121 Bosun-Josh121 merged commit 2482d34 into LightForgeHub:main Mar 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Role-Based Access Control (RBAC) - Moderators

2 participants