fix(auth): address round-3 review findings in roles controller (EVO-1061)#25
Conversation
…del (EVO-1061) - Check both granted and revoked permissions in bulk_update_permissions (M-R3-1) - Fix account_user_roles to include account_owner via Role.where instead of Role.account_type (L-R3-1) - Remove dead :key/:type from role_params permit; simplify create type to explicit account (L-R3-2) - Fix prevent_system_role_key_modification to use will_save_change_to_attribute? (L-R3-3) - Remove dead .except(:key, :type) from update action Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reviewer's guide (collapsed on small PRs)Reviewer's GuideUpdates the roles controller and Role model to tighten authorization around bulk permission updates, simplify role creation/update params, correct the account user roles lookup, and ensure system role key changes are properly blocked via the model callback. Sequence diagram for bulk_update_permissions authorization checksequenceDiagram
actor ApiUser
participant RolesController
participant Role
ApiUser->>RolesController: bulk_update_permissions
RolesController->>Role: permission_keys
RolesController->>RolesController: compute target_set
RolesController->>RolesController: compute current_set
RolesController->>RolesController: compute granted and revoked
RolesController->>RolesController: compute diffs
RolesController->>RolesController: compute unauthorized
alt [unauthorized.any?]
RolesController-->>ApiUser: error_response FORBIDDEN
else [no unauthorized]
RolesController->>Role: update permission_keys
RolesController-->>ApiUser: success response
end
Sequence diagram for system role key modification preventionsequenceDiagram
actor ApiUser
participant RolesController
participant Role
ApiUser->>RolesController: update
RolesController->>Role: update
Role->>Role: prevent_system_role_key_modification
alt [system? && will_save_change_to_attribute?(:key)]
Role->>Role: errors.add key
Role->>Role: throw abort
RolesController-->>ApiUser: render_unprocessable_entity
else [no system key change]
Role-->>RolesController: update success
RolesController-->>ApiUser: success response
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="app/models/role.rb" line_range="69" />
<code_context>
def prevent_system_role_key_modification
- if system? && saved_change_to_attribute?(:key)
+ if system? && will_save_change_to_attribute?(:key)
errors.add(:key, "cannot be modified for system roles")
throw :abort
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Using `will_save_change_to_attribute?` in this callback is correct, but consider if other code paths can still bypass the system-role key protection.
This change correctly makes the guard work in `before_*` callbacks. Please also check whether system-role keys are ever updated via callback-bypassing methods like `update_columns` or `update_all`. If so, this protection won’t apply there; you may need to centralize updates or enforce immutability via a database constraint instead.
Suggested implementation:
```ruby
end
def prevent_system_role_key_modification
if system? && will_save_change_to_attribute?(:key)
errors.add(:key, "cannot be modified for system roles")
throw :abort
end
end
# Ensure system roles are immutable at the ActiveRecord layer so that their
# attributes (including `key`) cannot be changed via callback-bypassing
# methods like `update_columns`.
def readonly?
system? || super
end
```
To fully implement the review suggestion:
1. Audit the codebase for any usage of `Role.update_all` or raw SQL updates touching system roles; those will still bypass model-level protections.
2. Consider adding a database-level constraint (e.g., a partial index or CHECK constraint) to prevent updates to the `key` column for rows where `system = TRUE`, which will also cover `update_all` and direct SQL.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
|
||
| def prevent_system_role_key_modification | ||
| if system? && saved_change_to_attribute?(:key) | ||
| if system? && will_save_change_to_attribute?(:key) |
There was a problem hiding this comment.
suggestion (bug_risk): Using will_save_change_to_attribute? in this callback is correct, but consider if other code paths can still bypass the system-role key protection.
This change correctly makes the guard work in before_* callbacks. Please also check whether system-role keys are ever updated via callback-bypassing methods like update_columns or update_all. If so, this protection won’t apply there; you may need to centralize updates or enforce immutability via a database constraint instead.
Suggested implementation:
end
def prevent_system_role_key_modification
if system? && will_save_change_to_attribute?(:key)
errors.add(:key, "cannot be modified for system roles")
throw :abort
end
end
# Ensure system roles are immutable at the ActiveRecord layer so that their
# attributes (including `key`) cannot be changed via callback-bypassing
# methods like `update_columns`.
def readonly?
system? || super
endTo fully implement the review suggestion:
- Audit the codebase for any usage of
Role.update_allor raw SQL updates touching system roles; those will still bypass model-level protections. - Consider adding a database-level constraint (e.g., a partial index or CHECK constraint) to prevent updates to the
keycolumn for rows wheresystem = TRUE, which will also coverupdate_alland direct SQL.
dpaes
left a comment
There was a problem hiding this comment.
Approved.
Round 3 findings (M-R3-1, L-R3-1, L-R3-2, L-R3-3) all closed correctly. The bulk_update_permissions intersection now covers both granted and revoked keys, account_user_roles returns account_owner, role_params is trimmed to (:name, :description), and prevent_system_role_key_modification uses will_save_change_to_attribute?.
One low-priority cleanup noted (the system-role guard at roles_controller.rb:53 still checks role_params.key?(:key), which is now always false since :key is no longer permitted — safe to drop that leg). Non-blocking; tracked in the Linear card.
Merging with --delete-branch.
Summary
bulk_update_permissions— caller cannot revoke keys they do not hold (M-R3-1)account_user_rolesto returnaccount_ownerrole viaRole.whereinstead ofRole.account_type.where(L-R3-1):key/:typefromrole_paramspermit; simplifycreatetype to explicit'account'(L-R3-2)prevent_system_role_key_modificationcallback to usewill_save_change_to_attribute?— was no-op withsaved_change_to_attribute?(L-R3-3).except(:key, :type)fromupdateactionValidation
evo-auth-service-community: ruby -c app/controllers/api/v1/roles_controller.rb→ Syntax OKevo-auth-service-community: ruby -c app/models/role.rb→ Syntax OKChanged Files
app/controllers/api/v1/roles_controller.rbapp/models/role.rbRelated PRs
Linked Issue
Summary by Sourcery
Address review feedback on role management by tightening permission checks, simplifying role parameters, and fixing system role protections.
Bug Fixes:
Enhancements: