Skip to content

Consolidate validation rules for same-org restrictions#16427

Open
AlanCoding wants to merge 4 commits intoansible:develfrom
AlanCoding:org_rule_update
Open

Consolidate validation rules for same-org restrictions#16427
AlanCoding wants to merge 4 commits intoansible:develfrom
AlanCoding:org_rule_update

Conversation

@AlanCoding
Copy link
Copy Markdown
Member

@AlanCoding AlanCoding commented Apr 23, 2026

SUMMARY

This has been causing a lot of test failures and been very difficult to troubleshoot.

I believe that the prior update to these rules, #16106, didn't do what was expected because that was changing the rules for the deprecated endpoints. The new, DAB RBAC, system has rules implemented in another place.

That other place (introduced in #15508) did some very strange backflips that are no longer necessary since the introduction of permission synchronization. So the extra logic there is removed.

Additionally, this shares the logic between the new and old endpoints so that they will give the same behavior. There are 6 endpoints where you might assign these permissions. But only 1 method in the new system for the callback because it is for both user/team. The old system has 4 points of interception in the views because of user vs team in forward vs reverse.

ISSUE TYPE
  • Bug, Docs Fix or other nominal change
COMPONENT NAME
  • API

Note

Medium Risk
Changes authorization/validation behavior for credential and execution environment role assignments (including cross-organization and private credential cases), which can affect who can be granted access. Risk is mitigated by updated functional tests but should be reviewed carefully for permission regressions.

Overview
Consolidates same-organization role-assignment validation by routing legacy RBAC endpoints (team/user/role attach flows) through each resource’s validate_role_assignment hook instead of endpoint-specific credential checks, and skips this validation on disassociate.

Removes resource-server-based membership backflips and updates Credential.validate_role_assignment to allow superusers (as the requesting user) to bypass same-org restrictions; similarly drops the resource-server exception in ExecutionEnvironment.validate_role_assignment. Functional tests are updated to reflect the new centralized behavior and revised status codes/messages for legacy endpoints and DAB RBAC cross-org assignment.

Reviewed by Cursor Bugbot for commit 70121bd. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • Bug Fixes

    • Standardized validation for credential and team role assignments, removing inconsistent inline checks.
    • Tightened organization-scoped permission checks; superusers retain broader assignment ability.
    • Adjusted error responses and status codes for denied role assignments.
  • Tests

    • Updated functional tests to reflect new authorization behavior and revised error payloads.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 26b63be7-66c2-4893-8a6a-ca8774e4dc10

📥 Commits

Reviewing files that changed from the base of the PR and between 92d8f9e and 70121bd.

📒 Files selected for processing (1)
  • awx/api/views/__init__.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • awx/api/views/init.py

📝 Walkthrough

Walkthrough

View-layer inline credential-to-target checks were removed and replaced by conditional delegation to model-level validate_role_assignment calls; model validation no longer consults the DAB resource-server fallback for organization checks; tests updated to reflect changed authorization responses.

Changes

Cohort / File(s) Summary
API Views & Validation Delegation
awx/api/views/__init__.py
Removes inline credential-to-target validation and ContentType-based checks from role-assignment endpoints. Instead, when not disassociating, the view conditionally calls role.content_object.validate_role_assignment(target, role_definition=None, requesting_user=request.user). For team-role attachments the delegation is only performed when action == 'attach'.
Model-level Validation Updates
awx/main/models/credential.py, awx/main/models/execution_environments.py
Deletes usages of check_resource_server_for_user_in_organization. Credential.validate_role_assignment now always reads requesting_user from kwargs and short-circuits for superusers; removes resource-server fallback for org membership checks. ExecutionEnvironment.validate_role_assignment no longer relies on resource-server-based org checks.
Tests Updated for Authorization & Responses
awx/main/tests/functional/api/test_credential.py, awx/main/tests/functional/dab_rbac/test_dab_rbac_api.py
Adjusts expected HTTP status codes and error payload keys for private-credential and cross-organization role-assignment cases (some 400→403 or 204; msgdetail), and distinguishes superuser vs non-superuser behavior in cross-org assignment test.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant View as API View
    participant Model as ContentObject (e.g., Credential / ExecEnv)
    participant OrgDB as Organization / Permissions DB

    Client->>View: POST role assignment (attach)
    View->>Model: validate_role_assignment(target, requesting_user)
    Model->>OrgDB: check organization membership / view permission
    OrgDB-->>Model: membership / permission result
    alt allowed
        Model-->>View: success
        View-->>Client: 204 / success
    else denied
        Model-->>View: raise ValidationError / deny
        View-->>Client: 403 / error detail
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Consolidate validation rules for same-org restrictions' is clear, specific, and directly aligns with the main change: removing duplicated same-organization validation logic and consolidating it across multiple endpoints.
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 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.

@AlanCoding AlanCoding marked this pull request as ready for review April 23, 2026 19:46
@AlanCoding
Copy link
Copy Markdown
Member Author

76.4% Coverage on New Code (required ≥ 80%)

For something that is mainly deleting in the main code path, that's just a messed up metric.

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@awx/api/views/__init__.py`:
- Around line 803-807: The code is incorrectly gating validate_role_assignment()
on the presence of the 'disassociate' key which allows payloads like
{"disassociate": false} to bypass attach validation; change the conditional to
check the action is an attach (reuse the same "action == 'attach'" pattern used
in RoleTeamsList) so validate_role_assignment(team, role_definition=None,
requesting_user=request.user) runs only for attach flows; update the same
pattern in the other occurrences that currently check 'disassociate' (the blocks
around the role/content_object calls referenced in the review) and ensure you
still obtain team via get_object_or_404(models.Team, pk=self.kwargs['pk']) and
call content_object.validate_role_assignment with the same arguments.
- Around line 803-807: The model hook calls to validate_role_assignment can
raise django.core.exceptions.ValidationError which isn't handled by the API
layer; wrap each call to content_object.validate_role_assignment (and any
similar delegation spots) in a try/except that catches
django.core.exceptions.ValidationError and re-raises it as
rest_framework.exceptions.ValidationError (converting message_dict/messages
appropriately) so the API returns a 400 instead of a 500; apply this same
wrapper around the validate_role_assignment invocations referenced (the call
inside the view method that fetches Team and content_object and the other
similar delegation sites).
🪄 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: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: c15505a1-7821-4875-973d-ed9f21506a74

📥 Commits

Reviewing files that changed from the base of the PR and between 99ac0d3 and 92d8f9e.

📒 Files selected for processing (5)
  • awx/api/views/__init__.py
  • awx/main/models/credential.py
  • awx/main/models/execution_environments.py
  • awx/main/tests/functional/api/test_credential.py
  • awx/main/tests/functional/dab_rbac/test_dab_rbac_api.py
💤 Files with no reviewable changes (1)
  • awx/main/models/execution_environments.py

Comment thread awx/api/views/__init__.py Outdated
Copy link
Copy Markdown
Contributor

@stevensonmichel stevensonmichel left a comment

Choose a reason for hiding this comment

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

The PR looks great to me. One point of clarification I might have. I see that check_resource_server_for_user_in_organization was removed. Is the purpose of this function handled elsewhere in the newest version (2.7, 2.6, ...) ?

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@AlanCoding
Copy link
Copy Markdown
Member Author

AlanCoding commented Apr 24, 2026

Baby YOLO Results — org_rule_update

Build: containerized 158— FAILURE, 26 failures, 2460 tests ran

Cross-org credential behavior changed as expected

Compared to devel reference builds (e.g. 146, 151), the cross-org credential RBAC tests changed form:

Test Devel behavior org_rule_update behavior
test_superuser_can_grant_team_cross_organization_credential_access (x4) FAIL — got 400 (blocked), expected 201 PASS — superuser cross-org assignment now succeeds
test_invalid_organization_credential_role_assignment (x4) FAIL — error message mismatch (got truncated message, expected message with "Only superusers can grant cross-organization credential access to teams" suffix) FAIL — got 201 (assignment allowed), expected 400 (blocked). The org rule update changed the behavior so non-superuser cross-org assignment is now allowed rather than blocked with a different error message

So this branch:

  • Fixed 4 tests (test_superuser_can_grant_team_cross_organization_credential_access) — superuser cross-org credential grants now work as the test expects
  • Changed the failure mode on 4 tests (test_invalid_organization_credential_role_assignment) — the assignment now succeeds (201) rather than failing with a truncated error message. These tests will need updating to match the new intended behavior

Other failures (22) — all pre-existing / flaky

The remaining 22 failures all appear in devel reference builds and are not caused by this branch:

  • 6x AzureAD ODataError (external service flake)
  • 8x EDA event stream IndexError: pop from empty list (EDA-side issue)
  • 2x test_team_can_change/delete_credentials — got 403, expected 200/204. These are the transient RBAC 403 pattern seen in devel reference builds, possibly caused by parallel test interference in compute_team_member_roles (DAB PR #980 may address this)
  • 3x Project failed to enter a successful state — flaky project sync timeouts in test setup
  • 1x hub e2e galaxy publish (7/10 reference fail rate)
  • 1x test_hostmetrics_host_metric_summary_monthly (6/10 reference fail rate)
  • 1x test_config_get EULA assertion (6/10 reference fail rate)

Summary

The cross-org credential behavior change is confirmed working. The test_superuser_can_grant_team_cross_organization_credential_access tests are now passing. The test_invalid_organization_credential_role_assignment tests need a test update to match the new intended behavior. No regressions introduced by this branch.

return safe_env


def check_resource_server_for_user_in_organization(user, organization, requesting_user):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

💣 🔫

Copy link
Copy Markdown
Member

@chrismeyersfsu chrismeyersfsu left a comment

Choose a reason for hiding this comment

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

I understand this change 👍 Re-use the existing validate_role_assignment that is present on the Credential model.

post(url=url, data={"user": rando.id, "role_definition": rd.id, "object_id": credential.id}, user=admin_user, expect=201)

# non-superuser (org_admin) cannot assign cross-org
resp = post(url=url, data={"user": rando.id, "role_definition": rd.id, "object_id": credential.id}, user=org_admin, expect=400)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Consider using a different user than rando, that user has already been given Credential Admin via the superuser.

This is a non-blocking comment, the assertion below checks for the reason the 400 is returned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants