feat(contract): add caller-addressed RBAC grants to payment_router - #847
Merged
Abdulazeem-code merged 2 commits intoOct 5, 2026
Merged
Conversation
Extend the existing RBAC system with an explicit admin/actor argument so role management can be authorized against a caller-supplied address rather than only the single designated signer the contract resolves for itself. - grant_role(admin, grantee, role) and revoke_role(admin, grantee, role), both SuperAdmin-gated; assign_role is retained as a compatibility alias - require_role_of(env, caller, role) checks a named address and returns Error::RoleNotFound before calling caller.require_auth() - has_role reports effective authority and stays in step with require_role (per-address grant, then designated holder, then root admin fallback) - set_role_internal clears a displaced holder's grant so a reassignment cannot leave has_role and require_role disagreeing - grant/revoke are idempotent and emit role_assigned/role_revoked - revoking the acting SuperAdmin's own root role is refused Regenerate the TypeScript bindings and add 11 RBAC tests. Snapshots are updated for the new role storage. Verification: cargo build and cargo test (102 passed, 0 failed, 3 ignored) in payment_router, and cargo fmt --all -- --check is clean. Refs Abdulazeem-code#715
|
Hey @kimanicode! 👋 It looks like this PR isn't linked to any issue. If this PR is for one of the issues assigned to you as part of a Wave, please link it to ensure your contribution is tracked properly. You can do this by adding a keyword to the PR description (e.g.,
|
|
@kimanicode is attempting to deploy a commit to the Abdulazeem's projects Team on Vercel. A member of the Team first needs to authorize it. |
Owner
|
Please resolve conflicts |
1 similar comment
Owner
|
Please resolve conflicts |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #715
Summary
Extends the existing RBAC system in
payment_routerwith an explicitadmin/actor argument, so role management is authorized against a
caller-supplied address instead of only the single designated signer the
contract resolves for itself.
Changes
New public API
grant_role(admin, grantee, role)— SuperAdmin-gatedrevoke_role(admin, grantee, role)— SuperAdmin-gatedassign_role(account, role)— retained as a compatibility alias forexisting callers; resolves the acting admin from contract state
New internals
require_role_of(env, caller, role)— checks a named address andreturns
Error::RoleNotFoundbefore callingcaller.require_auth()has_role_internal— reports effective authority and mirrors theresolution order of
require_role: per-address grant, then designatedholder, then root-admin fallback. An integration that consults
has_rolebefore building a transaction cannot be told "yes" for anaddress the contract then refuses, or "no" for one it accepts.
apply_role_grant/apply_role_revoke— shared, idempotent pathsemitting
role_assigned/role_revokedBug fix
set_role_internalnow clears a displaced holder'sUserRoleflag.Previously, reassigning a role left the previous holder reporting the
role through
has_rolewhile being unable to exercise it.Safety
SuperAdmin's own root role returnsError::InvalidRolerather than bricking contract administration.Also
revoke_roleis an ABI break (grantee and role are now explicit);TypeScript bindings regenerated via
npm run generate:bindingsscripts/generate-bindings.sh: resolve the wasm artifact from theworkspace target dir, with a fallback for standalone checkouts
payment_proxy/src/lib.rs: rustfmt onlyVerification
cargo build(payment_router)cargo test(payment_router)cargo fmt --all -- --checkstellar 27.1.0Please review — deviations and assumptions
These are deliberate, and worth a second opinion:
The new helper is
require_role_of, notrequire_role. Thecontract already had a private
require_role(env, role)that resolvesthe single designated signer, used by ~15 call sites. The new
caller-addressed helper was named alongside it rather than renaming the
existing one. Happy to invert the names if you prefer the spec's naming.
It returns
Err, it does not panic. A caller lacking the role getsError::RoleNotFoundback instead of a trap."Tests should panic on missing auth" is not testable in this SDK.
An unmatched
require_authaborts the test host withthread caused non-unwinding panic. aborting.— it is not catchable viatry_*orcatch_unwind. The tests instead assert the required signeridentity via
env.auths(), and the role rejection via exact-args mockauth.
One designated holder per role — a real behavioural limitation. The
pre-existing model is
DataKey::Role(Role) -> Address, and thepause/withdraw entrypoints take no caller argument, so enforcement has
nothing to check but the designated address. Granting a role to a new
address displaces the previous holder. The spec's "per-address role
set" reads as additive multi-holder grants; real multi-holder
semantics would need an
actor: Addressparameter onset_pause,set_min_limitand friends — a further ABI break. Not done here; happyto scope it if that is the intent.
Fee governance precedence is unchanged (assumption). When a
Governanceaddress is configured it is the exclusive fee authority;otherwise
FeeManagerapplies.Known pre-existing failure
cargo clippy --all-targets --all-features -- -D warningsfails with 2errors, both
clippy::manual_let_elsein the branch's uncommitted oraclecode that this PR does not touch:
manual_let_elsepostdates that code andrust-security.ymlpins floatingdtolnay/rust-toolchain@stable, so CI will fail on this regardless of thischange. Left alone as out of scope — it is a two-line
let...elserewriteeach if you want it fixed here.