chore: update & optimize admin user flows#294
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughDuplicated per-module ChangesAdmin Check Centralization and Resource Access
Gmail Credential Validation Refactor
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.py (1)
56-62:⚠️ Potential issue | 🟠 Major | ⚡ Quick winUnreachable condition:
data_filters and len(data_filters) < 1is always False.The condition
if data_filters and len(data_filters) < 1can never be True:
- If
data_filtersis truthy (e.g., a non-empty list), thenlen(data_filters) >= 1- If
data_filtersis falsy (empty list or None), the first part short-circuitsThis appears to be leftover from the removal of the 400 branch for non-admin missing
data_filtersmentioned in the AI summary. The intended check was likelyif not data_filters or len(data_filters) < 1or simplyif not data_filters.🐛 Proposed fix
- if data_filters and len(data_filters) < 1: + if not data_filters: return JSONResponse( status_code=status.HTTP_403_FORBIDDEN, content=response_formatter.buildErrorResponse( 'Data access not set for user' ), )🤖 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 56 - 62, The condition `if data_filters and len(data_filters) < 1:` in the response block for checking data access is logically impossible and will never execute. When data_filters is truthy, its length is always at least 1, making the second part always false. Replace this condition with `if not data_filters:` to correctly check if data_filters is empty or None, which appears to be the intended behavior for denying access when no data filters are set for the user.wavefront/server/modules/llm_inference_config_module/llm_inference_config_module/controllers/llm_inference_config_controller.py (1)
107-137:⚠️ Potential issue | 🟡 MinorAdd admin check to
get_llm_inference_configendpoint or document the intentional design.The endpoint
GET /v1/llm-inference-configs/{config_id}lacks an admin check (lines 109-118), while all other endpoints in this controller—including the list endpointGET /v1/llm-inference-configs(line 84)—require admin access. This creates an authorization inconsistency: users can retrieve a specific config by ID without admin privileges, but cannot list all configs.Either add the admin check to align with the list endpoint and other operations, or if this is intentional design (allowing non-admins to read configs), document the reasoning and consider restricting the response to sanitize sensitive fields.
🤖 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/llm_inference_config_module/llm_inference_config_module/controllers/llm_inference_config_controller.py` around lines 107 - 137, The get_llm_inference_config endpoint lacks an admin authorization check that is present in other endpoints of the same controller (such as the list endpoint). Add an admin permission check at the beginning of the get_llm_inference_config function to enforce consistent authorization across all endpoints, similar to how other operations in the controller are protected. Alternatively, if allowing non-admin access to specific configs is intentional, add clear documentation explaining the design decision and consider sanitizing sensitive fields from the response before returning it to the client.
🤖 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/services/user_service.py`:
- Line 150: At line 150, replace the role ID comparison logic
`current_admin_role_id in new_user_data.role_id` with a name-based check that
verifies if any assigned role is actually an admin role. Query the database for
all role IDs where Role.name equals ADMIN_ROLE_NAME (following the same pattern
as used in get_user_role_for_scope at line 107), then check if any role ID in
new_user_data.role_id matches one of those admin role IDs using set
intersection. This ensures the logic correctly identifies admin role assignments
by name rather than relying on a specific role ID.
---
Outside diff comments:
In
`@wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.py`:
- Around line 56-62: The condition `if data_filters and len(data_filters) < 1:`
in the response block for checking data access is logically impossible and will
never execute. When data_filters is truthy, its length is always at least 1,
making the second part always false. Replace this condition with `if not
data_filters:` to correctly check if data_filters is empty or None, which
appears to be the intended behavior for denying access when no data filters are
set for the user.
In
`@wavefront/server/modules/llm_inference_config_module/llm_inference_config_module/controllers/llm_inference_config_controller.py`:
- Around line 107-137: The get_llm_inference_config endpoint lacks an admin
authorization check that is present in other endpoints of the same controller
(such as the list endpoint). Add an admin permission check at the beginning of
the get_llm_inference_config function to enforce consistent authorization across
all endpoints, similar to how other operations in the controller are protected.
Alternatively, if allowing non-admin access to specific configs is intentional,
add clear documentation explaining the design decision and consider sanitizing
sensitive fields from the response before returning it to the client.
🪄 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: 0d0f8161-e302-4d8b-b3c1-b367b1763c37
📒 Files selected for processing (10)
wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.pywavefront/server/modules/llm_inference_config_module/llm_inference_config_module/controllers/llm_inference_config_controller.pywavefront/server/modules/plugins_module/plugins_module/controllers/authenticator_controller.pywavefront/server/modules/plugins_module/plugins_module/controllers/datasource_controller.pywavefront/server/modules/plugins_module/plugins_module/services/datasource_services.pywavefront/server/modules/user_management_module/user_management_module/constants/auth.pywavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.pywavefront/server/modules/user_management_module/user_management_module/controllers/user_controller.pywavefront/server/modules/user_management_module/user_management_module/services/user_service.pywavefront/server/modules/user_management_module/user_management_module/utils/user_utils.py
💤 Files with no reviewable changes (1)
- wavefront/server/modules/plugins_module/plugins_module/services/datasource_services.py
| current_admin_role_id: str, | ||
| response_formatter: ResponseFormatter, | ||
| ) -> JSONResponse: | ||
| is_reactivating_admin = current_admin_role_id in new_user_data.role_id |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if there can be multiple admin roles or if admin role is unique
rg -n "ADMIN_ROLE_NAME" --type py -C 3
# Check how admin roles are created/managed
ast-grep --pattern 'Role($$$name=$$$"admin"$$$)'Repository: rootflo/wavefront
Length of output: 4420
🏁 Script executed:
#!/bin/bash
# Get the full reactivate_user method context around line 150
sed -n '140,160p' wavefront/server/modules/user_management_module/user_management_module/services/user_service.py
# Also check the method signature and how new_user_data is structured
sed -n '143,155p' wavefront/server/modules/user_management_module/user_management_module/services/user_service.py
# Check what new_user_data.role_id actually is
rg -A 5 "new_user_data\s*=" wavefront/server/modules/user_management_module/user_management_module/services/user_service.py | head -20Repository: rootflo/wavefront
Length of output: 1379
🏁 Script executed:
#!/bin/bash
# Check the NewUser model/dataclass to confirm role_id is a list
rg -A 10 "class NewUser" wavefront/server/modules/user_management_module/ --type py
# Check if there's any constraint on unique role names
rg -n "role.*name.*unique\|unique.*role.*name" wavefront/ --type py -i
# Check how roles are created/managed to see if multiple admin roles are possible
rg -B 3 -A 3 "name.*=.*['\"]admin['\"]" wavefront/ --type py
# Check Role model for unique constraints
fd -e py -path "*models*" -name "*role*" | xargs grep -l "class Role" | head -3Repository: rootflo/wavefront
Length of output: 6095
🏁 Script executed:
#!/bin/bash
# Check how is_reactivating_admin is used in reactivate_user after line 150
sed -n '150,200p' wavefront/server/modules/user_management_module/user_management_module/services/user_service.py
# Check the Role model to see if name is unique
fd -e py -name "*.py" | xargs grep -l "class Role" | head -3Repository: rootflo/wavefront
Length of output: 2841
🏁 Script executed:
#!/bin/bash
# Check the Role model definition
find . -name "*.py" -type f | xargs grep -l "class Role" | xargs grep -A 20 "class Role"
# Check if role.name has a unique constraint
find . -name "*.py" -type f | xargs grep -B 5 -A 15 "class Role"Repository: rootflo/wavefront
Length of output: 10845
🏁 Script executed:
#!/bin/bash
# Check if there are any unique constraints on the role name field
cat ./wavefront/server/modules/db_repo_module/db_repo_module/models/role.py
# Check how roles are created to see if name uniqueness is enforced elsewhere
grep -r "unique.*role\|role.*unique" wavefront/server/modules/db_repo_module --include="*.py" -i
# Check how the admin role is managed during initialization/migration
grep -B 5 -A 5 "admin_role_id" wavefront/server/modules/db_repo_module/db_repo_module/alembic/versions/*.py | head -50Repository: rootflo/wavefront
Length of output: 5386
Use name-based check to detect if any assigned role is an admin role.
Line 150's check current_admin_role_id in new_user_data.role_id skips console resource validation only if the caller's specific role ID is assigned to the new user. This logic is fragile because:
- The
Role.namefield has no unique constraint—multiple roles could theoretically havename='admin' - It doesn't verify if any assigned role is actually an admin role; it only checks if the caller's specific role ID is present
- If a non-admin role ID is accidentally passed as
current_admin_role_id, console validation would be incorrectly skipped
Instead, query for admin roles by name (as in get_user_role_for_scope at line 107), checking if any role in new_user_data.role_id has name == ADMIN_ROLE_NAME:
Suggested approach
# Query for admin role IDs
admin_stmt = (
select(Role.id)
.where(Role.name == ADMIN_ROLE_NAME)
)
admin_result = await session.execute(admin_stmt)
admin_role_ids = {str(role_id) for role_id in admin_result.scalars().all()}
# Check if any assigned role is an admin role
is_reactivating_admin = bool(set(new_user_data.role_id) & admin_role_ids)🤖 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/services/user_service.py`
at line 150, At line 150, replace the role ID comparison logic
`current_admin_role_id in new_user_data.role_id` with a name-based check that
verifies if any assigned role is actually an admin role. Query the database for
all role IDs where Role.name equals ADMIN_ROLE_NAME (following the same pattern
as used in get_user_role_for_scope at line 107), then check if any role ID in
new_user_data.role_id matches one of those admin role IDs using set
intersection. This ensures the logic correctly identifies admin role assignments
by name rather than relying on a specific role ID.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/services/email_service.py`:
- Around line 150-156: Change the exception type raised in the
_assert_credentials method from generic Exception to ValueError. The downstream
error handler in user_controller.py (line 486) specifically catches ValueError
for credential validation failures, and using the correct exception type ensures
the error handling contract is properly maintained.
🪄 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: 495f7f3c-0c93-4b47-9f2c-2a247896e912
📒 Files selected for processing (1)
wavefront/server/modules/user_management_module/user_management_module/services/email_service.py
| def _assert_credentials(self): | ||
| if not all( | ||
| [self.client_id, self.client_secret, self.refresh_token, self.email_sender] | ||
| ): | ||
| raise Exception( | ||
| 'Gmail OAuth requires client_id, client_secret, refresh_token, and email_sender' | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Find catch blocks that expect ValueError from email service operations.
# Search for ValueError exception handlers near email service calls
rg -nP -A5 -B5 'except\s+ValueError' --type=py | rg -A5 -B5 'email_service'Repository: rootflo/wavefront
Length of output: 43
🏁 Script executed:
# First, let's locate the user_controller.py file mentioned in the review
fd -t f 'user_controller.py' -x cat -n {} | head -50Repository: rootflo/wavefront
Length of output: 1958
🏁 Script executed:
# Check the email_service.py file to see the _assert_credentials method and its context
fd -t f 'email_service.py' -x wc -l {}Repository: rootflo/wavefront
Length of output: 164
🏁 Script executed:
# Find all files in user_management_module to understand the structure
fd -t f . 'wavefront/server/modules/user_management_module' | head -20Repository: rootflo/wavefront
Length of output: 1904
🏁 Script executed:
# Let's check user_controller.py around the lines mentioned (410-493)
cat -n wavefront/server/modules/user_management_module/user_management_module/controllers/user_controller.py | sed -n '400,500p'Repository: rootflo/wavefront
Length of output: 4538
🏁 Script executed:
# Let's search for all calls to _assert_credentials to see how it's used
rg -n '_assert_credentials' --type=py -A3 -B3Repository: rootflo/wavefront
Length of output: 1898
🏁 Script executed:
# Let's also search for where email_service is used and what exceptions are caught
rg -n 'email_service' --type=py -A5 -B5Repository: rootflo/wavefront
Length of output: 20649
🏁 Script executed:
# Let's look at the send_forget_password_email method in email_service.py
rg -n 'send_forget_password_email' --type=py -A15Repository: rootflo/wavefront
Length of output: 12637
🏁 Script executed:
# Let's also look at the get_credentials method to confirm it calls _assert_credentials
rg -n 'def get_credentials' --type=py -A15Repository: rootflo/wavefront
Length of output: 5675
🏁 Script executed:
# Let's check the _get_gmail_service method to see if it calls get_credentials
rg -n '_get_gmail_service' --type=py -A20Repository: rootflo/wavefront
Length of output: 7042
🏁 Script executed:
# Let's also read the full email_service.py to understand both implementations completely
cat -n wavefront/server/modules/user_management_module/user_management_module/services/email_service.py | head -100Repository: rootflo/wavefront
Length of output: 4302
🏁 Script executed:
# Let's see the complete send_forget_password_email method for GmailEmailService with its exception handling
sed -n '177,210p' wavefront/server/modules/user_management_module/user_management_module/services/email_service.pyRepository: rootflo/wavefront
Length of output: 1568
🏁 Script executed:
# Let's also check if there's an except clause for send_forget_password_email
sed -n '177,220p' wavefront/server/modules/user_management_module/user_management_module/services/email_service.pyRepository: rootflo/wavefront
Length of output: 1800
Change _assert_credentials() to raise ValueError instead of generic Exception.
The method raises a generic Exception, but the reset-password endpoint in user_controller.py (line 486) specifically catches ValueError to handle credential validation failures. This exception type mismatch breaks the error handling contract—credential failures should be raised as ValueError to be properly caught by downstream handlers expecting that specific type.
Proposed fix
def _assert_credentials(self):
if not all(
[self.client_id, self.client_secret, self.refresh_token, self.email_sender]
):
- raise Exception(
+ raise ValueError(
'Gmail OAuth requires client_id, client_secret, refresh_token, and email_sender'
)🤖 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/services/email_service.py`
around lines 150 - 156, Change the exception type raised in the
_assert_credentials method from generic Exception to ValueError. The downstream
error handler in user_controller.py (line 486) specifically catches ValueError
for credential validation failures, and using the correct exception type ensures
the error handling contract is properly maintained.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
wavefront/server/modules/user_management_module/user_management_module/controllers/user_controller.py (1)
81-81:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSame fragile admin detection as
reactivate_user.The check
role_id in new_user.role_idonly verifies whether the caller's specific role ID is being assigned, not whether any assigned role is actually an admin role. This mirrors the issue flagged inuser_service.pyline 216. Consider applying a name-based admin check consistently in both locations.🤖 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/user_controller.py` at line 81, The admin detection check in the method containing the `is_creating_admin = role_id in new_user.role_id` statement is fragile because it only verifies whether the caller's specific role ID is being assigned, rather than checking if any of the assigned roles are actually admin roles by name. Replace this check with a name-based admin role validation that inspects the actual role names in the new_user.role_id assignment to determine if any of them designate admin privileges, ensuring consistent admin detection logic across both this location and the similar check in the reactivate_user method.wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py (3)
91-101:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRedundant
session.commit()insidesession.begin()context manager.The
async with session.begin()context manager automatically commits on successful exit. The explicitawait session.commit()on line 101 is redundant and may cause warnings in some SQLAlchemy configurations.Proposed fix
async with resource_repository.session() as session: async with session.begin(): await resource_repository.create_all( resources, replace=True, session=session ) await role_repository.create_all(roles, replace=True, session=session) await role_resource_repository.create_all( role_resources, replace=True, session=session ) - await session.commit() - resource_count = len(payload.resources) + + return JSONResponse( + status_code=status.HTTP_201_CREATED, + content=response_formatter.buildSuccessResponse( + data={'message': f'Created {resource_count} resources successfully'} + ), + ) - return JSONResponse( - status_code=status.HTTP_201_CREATED, - content=response_formatter.buildSuccessResponse( - data={'message': f'Created {resource_count} resources successfully'} - ), - )🤖 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 91 - 101, The explicit `await session.commit()` call is redundant because the `async with session.begin()` context manager automatically commits when exiting the block successfully. Remove the `await session.commit()` line at the end of the session.begin() block (after the role_resource_repository.create_all() call) to eliminate the redundancy and avoid potential SQLAlchemy warnings about double commits.
266-276:⚠️ Potential issue | 🟠 Major | ⚡ Quick winColumn validation raises
AttributeErrorinstead of returning 400.
getattr(Role, item)raisesAttributeErrorfor non-existent attributes rather than returning a falsy value. The current checkif not getattr(Role, item)will never catch invalid columns—it will crash with an unhandled exception, resulting in a 500 error instead of the intended 400 response.Proposed fix using hasattr
item_to_select = select_item.split(',') if select_item else [] valid_columns = [] for item in item_to_select: - if not getattr(Role, item): + if not hasattr(Role, item): return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, content=response_formatter.buildErrorResponse( error=f'Invalid column {item}' ), ) valid_columns.append(getattr(Role, item))🤖 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 266 - 276, The column validation logic uses getattr(Role, item) without a default value, which raises AttributeError for non-existent attributes instead of returning a falsy value. This causes an unhandled exception and 500 error instead of the intended 400 response. Replace the getattr(Role, item) check in the condition with hasattr(Role, item) to safely verify the attribute exists before accessing it, ensuring invalid columns are properly caught and the JSONResponse with HTTP 400 is returned as intended.
206-227:⚠️ Potential issue | 🟠 Major | ⚡ Quick winType mismatch:
scopesannotation islist[str]but service expectsList[ResourceScope].The
scopesparameter is annotated aslist[str], but the default usesResourceScopeenum values anduser_service.get_all_resourcesexpectsList[ResourceScope]. When clients pass query parameters (e.g.,?scopes=DASHBOARD), FastAPI deserializes them as strings, not enum values, which may cause filtering to fail silently or behave incorrectly.Proposed fix
- scopes: list[str] = Query( + scopes: list[ResourceScope] = Query( default=[ResourceScope.DASHBOARD, ResourceScope.CONSOLE], description='The scopes of the resources to fetch', ),🤖 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 206 - 227, The scopes parameter type annotation is list[str] but should be List[ResourceScope] or list[ResourceScope] to match what the user_service.get_all_resources method expects and to properly align with the default values using ResourceScope enum members like ResourceScope.DASHBOARD and ResourceScope.CONSOLE. Change the type annotation from list[str] to the appropriate ResourceScope collection type so that FastAPI will automatically deserialize incoming query string parameters into ResourceScope enum values rather than leaving them as strings, ensuring the service receives the correct type and filtering works as intended.wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.py (1)
59-65:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winAuthorization check is unreachable due to always-false condition.
The condition
data_filters and len(data_filters) < 1can never be true:
- If
data_filtersis empty ([]), it's falsy → condition short-circuits toFalse- If
data_filtersis non-empty,len(data_filters) < 1isFalse→ condition isFalseWith the prior 400 validation branch removed, this unreachable 403 check is the only protection for non-admin users without data access. They will incorrectly proceed to guest token generation.
🔒 Proposed fix
- if data_filters and len(data_filters) < 1: + if not is_admin and not data_filters: return JSONResponse( status_code=status.HTTP_403_FORBIDDEN, content=response_formatter.buildErrorResponse( 'Data access not set for user' ), )🤖 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 authorization check at the superset_controller.py file uses a flawed logical condition with `and` that makes it unreachable. When data_filters is empty it's falsy (short-circuiting the condition to False), and when non-empty the length check becomes False. Change the logical operator from `and` to `or` in the condition so it properly validates whether data_filters is either empty or has zero elements, ensuring non-admin users without data access are correctly blocked from proceeding to guest token generation.
🤖 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.
Outside diff comments:
In
`@wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.py`:
- Around line 59-65: The authorization check at the superset_controller.py file
uses a flawed logical condition with `and` that makes it unreachable. When
data_filters is empty it's falsy (short-circuiting the condition to False), and
when non-empty the length check becomes False. Change the logical operator from
`and` to `or` in the condition so it properly validates whether data_filters is
either empty or has zero elements, ensuring non-admin users without data access
are correctly blocked from proceeding to guest token generation.
In
`@wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py`:
- Around line 91-101: The explicit `await session.commit()` call is redundant
because the `async with session.begin()` context manager automatically commits
when exiting the block successfully. Remove the `await session.commit()` line at
the end of the session.begin() block (after the
role_resource_repository.create_all() call) to eliminate the redundancy and
avoid potential SQLAlchemy warnings about double commits.
- Around line 266-276: The column validation logic uses getattr(Role, item)
without a default value, which raises AttributeError for non-existent attributes
instead of returning a falsy value. This causes an unhandled exception and 500
error instead of the intended 400 response. Replace the getattr(Role, item)
check in the condition with hasattr(Role, item) to safely verify the attribute
exists before accessing it, ensuring invalid columns are properly caught and the
JSONResponse with HTTP 400 is returned as intended.
- Around line 206-227: The scopes parameter type annotation is list[str] but
should be List[ResourceScope] or list[ResourceScope] to match what the
user_service.get_all_resources method expects and to properly align with the
default values using ResourceScope enum members like ResourceScope.DASHBOARD and
ResourceScope.CONSOLE. Change the type annotation from list[str] to the
appropriate ResourceScope collection type so that FastAPI will automatically
deserialize incoming query string parameters into ResourceScope enum values
rather than leaving them as strings, ensuring the service receives the correct
type and filtering works as intended.
In
`@wavefront/server/modules/user_management_module/user_management_module/controllers/user_controller.py`:
- Line 81: The admin detection check in the method containing the
`is_creating_admin = role_id in new_user.role_id` statement is fragile because
it only verifies whether the caller's specific role ID is being assigned, rather
than checking if any of the assigned roles are actually admin roles by name.
Replace this check with a name-based admin role validation that inspects the
actual role names in the new_user.role_id assignment to determine if any of them
designate admin privileges, ensuring consistent admin detection logic across
both this location and the similar check in the reactivate_user method.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4ecc0167-0008-403e-b76a-6aac232c0c5b
📒 Files selected for processing (4)
wavefront/server/modules/auth_module/auth_module/controllers/superset_controller.pywavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.pywavefront/server/modules/user_management_module/user_management_module/controllers/user_controller.pywavefront/server/modules/user_management_module/user_management_module/services/user_service.py
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 206-215: The scopes parameter in the controller function accepts
strings from the HTTP query (list[str]) but the service method expects
ResourceScope enum members (List[ResourceScope]). Locate where scopes are
extracted from the query parameter in the function signature and add conversion
logic to transform the string values into ResourceScope enum members before
passing them to the user_service.get_all_resources() call. This conversion
should handle the case where scopes is None or empty, using a pattern like
iterating through the scopes list and constructing ResourceScope enum values
from each string.
🪄 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: 312f6a92-d4af-45f6-9686-eb8e3d9e58b0
📒 Files selected for processing (1)
wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py
| scopes: Optional[list[str]] = Query( | ||
| default=None, | ||
| description='Scopes of the resources to fetch (all scopes when omitted)', | ||
| ), | ||
| search: Optional[str] = Query( | ||
| None, description='Search by key, value or description' | ||
| ), | ||
| limit: Optional[int] = Query( | ||
| None, description='Maximum number of resources to return (all when omitted)' | ||
| ), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify ResourceScope enum backing type to confirm string coercion works
echo "=== Checking ResourceScope enum definition ==="
rg -n -A10 'class ResourceScope' --type py
echo -e "\n=== Checking _resource_filters implementation for scope handling ==="
ast-grep --pattern $'def _resource_filters($$$):
$$$
'Repository: rootflo/wavefront
Length of output: 1322
🏁 Script executed:
#!/bin/bash
# Find get_all_resources function signature
echo "=== Finding get_all_resources function ==="
rg -n "def get_all_resources" --type py -A 5
echo -e "\n=== Finding _resource_filters function ==="
rg -n "def _resource_filters" --type py -A 15
echo -e "\n=== Checking imports in access_controller.py to see what ResourceScope is used ==="
head -50 wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py | grep -n "import\|from"Repository: rootflo/wavefront
Length of output: 4670
Type annotation mismatch requires fixing: scopes parameter accepts list[str] but service expects List[ResourceScope].
The controller accepts scopes: Optional[list[str]] from the HTTP query and passes it directly to user_service.get_all_resources(scopes=scopes, ...), which expects scopes: Optional[List[ResourceScope]]. While ResourceScope(str, Enum) means enum values are strings at runtime and Resource.scope.in_(scopes) will accept string values, the type annotation mismatch indicates missing validation/conversion logic.
Convert the query string values to ResourceScope enum members before passing to the service (e.g., using a FastAPI validator or list comprehension like [ResourceScope(s) for s in scopes if s]).
Regarding B008 warning: This is a false positive. FastAPI requires Query() calls in function parameter defaults for dependency injection—this is the documented pattern.
🧰 Tools
🪛 Ruff (0.15.15)
[warning] 206-209: Do not perform function call Query in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
🤖 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 206 - 215, The scopes parameter in the controller function accepts
strings from the HTTP query (list[str]) but the service method expects
ResourceScope enum members (List[ResourceScope]). Locate where scopes are
extracted from the query parameter in the function signature and add conversion
logic to transform the string values into ResourceScope enum members before
passing them to the user_service.get_all_resources() call. This conversion
should handle the case where scopes is None or empty, using a pattern like
iterating through the scopes list and constructing ResourceScope enum values
from each string.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py (1)
252-254: 💤 Low valueType annotation and default value inconsistency for
scopes.The default value
[ResourceScope.CONSOLE]uses the enum type, but the type annotation declareslist[str]. This works at runtime becauseResourceScopeextendsstr, but creates inconsistency for type checkers and IDE tooling.Consider using
Optional[list[str]]with aNonedefault and handling the default value inside the function, or explicitly document that enum values are acceptable:- scopes: list[str] = Query( - default=[ResourceScope.CONSOLE], description='The scopes of the roles to fetch' - ), + scopes: Optional[list[str]] = Query( + default=None, description='The scopes of the roles to fetch (defaults to CONSOLE)' + ),Then apply the default inside the function:
scopes = scopes or [ResourceScope.CONSOLE.value]🤖 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 252 - 254, The `scopes` parameter in the access_controller.py file has a type annotation inconsistency where it declares `list[str]` but uses `ResourceScope.CONSOLE` (an enum) as the default value. To fix this, change the type annotation for the `scopes` parameter to `Optional[list[str]]` with a default value of `None`, then add logic inside the function body to set `scopes` to `[ResourceScope.CONSOLE.value]` when the parameter is `None` (using a pattern like `scopes = scopes or [ResourceScope.CONSOLE.value]`). This ensures the type annotation accurately reflects what the function accepts and properly handles the default case inside the function logic.
🤖 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.
Nitpick comments:
In
`@wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py`:
- Around line 252-254: The `scopes` parameter in the access_controller.py file
has a type annotation inconsistency where it declares `list[str]` but uses
`ResourceScope.CONSOLE` (an enum) as the default value. To fix this, change the
type annotation for the `scopes` parameter to `Optional[list[str]]` with a
default value of `None`, then add logic inside the function body to set `scopes`
to `[ResourceScope.CONSOLE.value]` when the parameter is `None` (using a pattern
like `scopes = scopes or [ResourceScope.CONSOLE.value]`). This ensures the type
annotation accurately reflects what the function accepts and properly handles
the default case inside the function logic.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9dd753ca-78de-4ec9-b730-780a62435d93
📒 Files selected for processing (1)
wavefront/server/modules/user_management_module/user_management_module/controllers/access_controller.py
Summary by CodeRabbit
totalcounts, with admins receiving full results while non-admins receive role-scoped data.data_filtersrejection for non-admins, retaining the laterdata_filtersauthorization/validation behavior.