Skip to content

fix: composite role update#298

Open
vishnurk6247 wants to merge 1 commit into
developfrom
fix/rbac-bugs
Open

fix: composite role update#298
vishnurk6247 wants to merge 1 commit into
developfrom
fix/rbac-bugs

Conversation

@vishnurk6247

@vishnurk6247 vishnurk6247 commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added authorization check that denies access when data filters are not configured for a user.
    • Implemented stricter console role protection rules.
  • Bug Fixes

    • Improved orphaned role cleanup following resource deletion.
    • Enhanced duplicate role detection logic.
  • Tests

    • Added comprehensive test coverage for console role protection and resource operations.

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Three behavioral changes across two modules: superset_controller adds a 403 guard when data_filters is empty; access_controller fixes role-duplicate detection to use exact HAVING equality, adds orphaned-role cleanup when a resource is deleted, and narrows console-role edit/delete protection to exclusively-console roles only.

Changes

User Management: Role Protection and Resource Cleanup

Layer / File(s) Summary
Exact role-duplicate detection via HAVING
wavefront/.../access_controller.py
Adds SQLAlchemy delete import and replaces the duplicate-role check with a two-part HAVING clause requiring both total resource count and matched payload count to equal the payload size, blocking superset roles from matching as duplicates.
Orphaned role cleanup on resource deletion
wavefront/.../access_controller.py
Upgrades delete_resources to a DB transaction that collects linked role IDs before deleting the resource, then explicitly deletes any roles that become orphaned (no remaining resource links).
Narrowed console-role edit/delete protection
wavefront/.../access_controller.py
Changes patch_role_resources and delete_role to reject only exclusively console-scoped (single-resource) roles; composite roles that include a console resource are now permitted to be updated or deleted.
Tests for console-role PATCH and DELETE rules
wavefront/.../tests/test_access_controller.py
Adds four async tests covering: PATCH rejection (400) on a pure console role, PATCH success (200) on a composite console role with correct DB state, DELETE rejection (400) on a pure console role, and DELETE success (200) on a composite console role.

Superset Auth: Missing data_filters Guard

Layer / File(s) Summary
403 guard for missing data_filters
wavefront/.../superset_controller.py
Adds an if not data_filters: early return in superset_authenticator that responds with HTTP 403 and an error indicating data access is not configured for the user.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 A console role stood tall, just one in its den,
Composite cousins may edit and then—
Orphaned roles vanish when resources depart,
And filters unchecked get a 403 start.
The HAVING clause counts every resource with care,
No superset imposters shall sneak in there! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: composite role update' directly relates to the main change: allowing composite roles (roles with multiple resources including console) to be updated, while blocking only pure console-scoped roles.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/rbac-bugs

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.py (1)

59-65: ⚡ Quick win

Consider aligning HTTP status codes for consistency.

The new 403 FORBIDDEN is semantically correct for an authorization issue. However, the earlier dashboard check at line 49 returns 400 BAD_REQUEST for a similar authorization concern ("User does not have access to any dashboard"). Both situations represent access/permission issues and should ideally use the same status code.

Since 403 FORBIDDEN more accurately represents authorization failures, consider updating the dashboard check (lines 47-53) to also use status.HTTP_403_FORBIDDEN for consistency.

📝 Suggested alignment for consistency
     if not dashboards:
         return JSONResponse(
-            status_code=status.HTTP_400_BAD_REQUEST,
+            status_code=status.HTTP_403_FORBIDDEN,
             content=response_formatter.buildErrorResponse(
                 'User does not have access to any dashboard'
             ),
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.py`
around lines 59 - 65, The HTTP status codes for authorization failures are
inconsistent. The data_filters check correctly uses status.HTTP_403_FORBIDDEN,
but the earlier dashboard access check uses status.HTTP_400_BAD_REQUEST for the
message "User does not have access to any dashboard". Both represent
authorization/permission failures and should use the same status code. Update
the dashboard access check to use status.HTTP_403_FORBIDDEN instead of
status.HTTP_400_BAD_REQUEST to align with the data_filters check and properly
represent authorization failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py`:
- Around line 535-567: Before executing the delete(Role) statement that removes
roles where Role.id.in_(orphaned_role_ids), add a check to query the UserRole
table to see if any users are assigned to those orphaned roles. If user-role
associations exist for the orphaned_role_ids, either prevent the deletion from
occurring or add appropriate logging to explicitly document that users are
losing role assignments, making the behavior transparent rather than silently
cascading the deletion.

---

Nitpick comments:
In
`@wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.py`:
- Around line 59-65: The HTTP status codes for authorization failures are
inconsistent. The data_filters check correctly uses status.HTTP_403_FORBIDDEN,
but the earlier dashboard access check uses status.HTTP_400_BAD_REQUEST for the
message "User does not have access to any dashboard". Both represent
authorization/permission failures and should use the same status code. Update
the dashboard access check to use status.HTTP_403_FORBIDDEN instead of
status.HTTP_400_BAD_REQUEST to align with the data_filters check and properly
represent authorization failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0ba203f2-45a7-460e-ae2b-48c55f1ddb85

📥 Commits

Reviewing files that changed from the base of the PR and between ae1c694 and 8659b86.

📒 Files selected for processing (3)
  • wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.py
  • wavefront/server/modules/user_management_module/tests/test_access_controller.py
  • wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py

Comment on lines +535 to +567

async with resource_repository.session() as session:
async with session.begin():
linked_role_ids = (
await session.scalars(
select(RoleResource.role_id).where(
RoleResource.resource_id == resource_id
)
)
).all()

# Deleting the resource cascades its role_resource links.
await resource_repository.delete_all(id=resource_id, session=session)

if linked_role_ids:
remaining_role_ids = set(
(
await session.scalars(
select(RoleResource.role_id).where(
RoleResource.role_id.in_(linked_role_ids)
)
)
).all()
)
orphaned_role_ids = [
role_id
for role_id in linked_role_ids
if role_id not in remaining_role_ids
]
if orphaned_role_ids:
await session.execute(
delete(Role).where(Role.id.in_(orphaned_role_ids))
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how User-Role relationship is defined and whether cascading is handled
ast-grep --pattern $'class User($_) {
  $$$
  role$_
  $$$
}'

# Also search for any user_role join table or relationship definition
rg -n "user.*role|role.*user" --type py -g '*/models/*' -C 3

Repository: rootflo/wavefront

Length of output: 210


🏁 Script executed:

# Find the repository structure
git ls-files | grep -E "(model|user|role)" | head -20

Repository: rootflo/wavefront

Length of output: 1033


🏁 Script executed:

# Look for Python files that might contain User and Role class definitions
fd -e py -type f | grep -iE "(model|user|role)" | head -20

Repository: rootflo/wavefront

Length of output: 232


🏁 Script executed:

# Check the directory structure of the user_management_module
find wavefront/server/modules/user_management_module -type f -name "*.py" | head -30

Repository: rootflo/wavefront

Length of output: 2213


🏁 Script executed:

# Examine the resource model file which likely contains Role and RoleResource
cat -n wavefront/server/modules/user_management_module/user_management_module/models/resource.py

Repository: rootflo/wavefront

Length of output: 2401


🏁 Script executed:

# Examine the user schema file which likely contains User model
cat -n wavefront/server/modules/user_management_module/user_management_module/models/user_schema.py

Repository: rootflo/wavefront

Length of output: 6512


🏁 Script executed:

# Search for where Role model is defined and check for user_role relationship
rg -n "class Role\b|class RoleResource\b" wavefront/server/modules/user_management_module --type py -A 20

Repository: rootflo/wavefront

Length of output: 2446


🏁 Script executed:

# Search for SQLAlchemy imports and model definitions
rg -n "from sqlalchemy|import.*Column|class.*db\.|declarative_base" wavefront/server/modules/user_management_module --type py | head -20

Repository: rootflo/wavefront

Length of output: 2692


🏁 Script executed:

# Search specifically for RoleResource class definition
rg -n "class RoleResource" wavefront/server/modules/user_management_module --type py -A 15

Repository: rootflo/wavefront

Length of output: 43


🏁 Script executed:

# Look in the entire user_management_module for all Python files to understand structure
find wavefront/server/modules/user_management_module -name "*.py" -type f | xargs grep -l "sqlalchemy\|declarative_base\|Column"

Repository: rootflo/wavefront

Length of output: 665


🏁 Script executed:

# Check imports in access_controller.py to find where models come from
head -50 wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py

Repository: rootflo/wavefront

Length of output: 1821


🏁 Script executed:

# Check conftest to see how models are loaded
cat -n wavefront/server/modules/user_management_module/tests/conftest.py

Repository: rootflo/wavefront

Length of output: 12171


🏁 Script executed:

# Search for all Python files containing RoleResource or Role ORM model in the broader codebase
find wavefront -name "*.py" -type f | xargs grep -l "class RoleResource\|class.*Role.*:" | grep -v __pycache__ | head -20

Repository: rootflo/wavefront

Length of output: 437


🏁 Script executed:

# Examine the Role ORM model
cat -n wavefront/server/modules/db_repo_module/db_repo_module/models/role.py

Repository: rootflo/wavefront

Length of output: 1104


🏁 Script executed:

# Examine the RoleResource ORM model
cat -n wavefront/server/modules/db_repo_module/db_repo_module/models/role_resource.py

Repository: rootflo/wavefront

Length of output: 621


🏁 Script executed:

# Examine the User-Role relationship model
cat -n wavefront/server/modules/db_repo_module/db_repo_module/models/user_role.py

Repository: rootflo/wavefront

Length of output: 605


🏁 Script executed:

# Also check if there's a User model to see all relationships
find wavefront/server/modules/db_repo_module -name "*.py" -type f | xargs grep -l "class User" | head -5

Repository: rootflo/wavefront

Length of output: 206


The orphan role deletion is safe regarding foreign-key constraints but may silently remove users' role assignments.

The UserRole table has ondelete='CASCADE' configured (ForeignKey on role_id), so deleting a role automatically cleans up user-role associations at the database level without causing FK violations. However, users assigned to orphaned roles will lose their role assignments silently, with no warning or compensation. If role deletion should be prevented when users are assigned, add a check to validate this before deletion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py`
around lines 535 - 567, Before executing the delete(Role) statement that removes
roles where Role.id.in_(orphaned_role_ids), add a check to query the UserRole
table to see if any users are assigned to those orphaned roles. If user-role
associations exist for the orphaned_role_ids, either prevent the deletion from
occurring or add appropriate logging to explicitly document that users are
losing role assignments, making the behavior transparent rather than silently
cascading the deletion.

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.

1 participant