Skip to content

[AAP-64918] Adds AuditableModel for Role*Assignment#956

Open
huffmanca wants to merge 2 commits intoansible:develfrom
huffmanca:AAP-64918
Open

[AAP-64918] Adds AuditableModel for Role*Assignment#956
huffmanca wants to merge 2 commits intoansible:develfrom
huffmanca:AAP-64918

Conversation

@huffmanca
Copy link
Contributor

@huffmanca huffmanca commented Feb 25, 2026

Description

Adds AuditableModel to AssignmentBase so that it can be leveraged by RoleUserAssignment and RoleTeamAssignment. This is configured to have audit logs created for these classes without updating the ActivityStream.

These are similar changes to PR 945 , just for the RoleUserAssignment / RoleTeamAssignment (through AssignmentBase).

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Test update
  • Refactoring (no functional changes)
  • Development environment change
  • Configuration change

Self-Review Checklist

  • I have performed a self-review of my (and Claude's) code
  • I have added relevant comments to complex code sections
  • I have updated documentation where needed
  • I have considered the security impact of these changes
  • I have considered performance implications
  • I have thought about error handling and edge cases
  • I have tested the changes in my local environment

Testing Instructions

Steps to Test

  1. Start an AAP instance with these changes.
  2. Create RoleUserAssignment/RoleTeamAssignment objects.
  3. Ensure that the Gateway logs have entries indicating that the models were created. Sample log lines are described below.
  4. Ensure that no ActivityStream entries are created.

Expected Results

We should see lines similar to the following in the logs:

myaap-gateway-5fc8fd7844-tsl9g api 2026-02-25T14:48:10.022072188-05:00 2026-02-25 19:48:10,021 INFO     3c800b99-b104-428e-8046-eb4fc65d907b aap.auth_audit [10.244.0.1] "Mozilla/5.0 (X11; Linux x86_64; rv:146.0) Gecko/20100101 Firefox/146.0" User: admin create RoleUserAssignment RoleUserAssignment object (18) (18) {'object_role': '4', 'created': '2026-02-25T19:48:10.020024+00:00', 'content_type': '11', 'object_id': '214', 'role_definition': '2', 'id': '18', 'created_by': '2', 'user': '18'}

No ActivityStream entries should be created

Additional Context

As mentioned in the description, this is similar to the approach in PR 945 .

Summary by CodeRabbit

  • New Features
    • Enabled audit logging for role assignment operations with conditional activity stream integration (defaults to audit enabled; activity stream disabled when not available).
  • Tests
    • Added tests verifying audit logs are emitted on create/delete of role assignments and that no activity stream entries are created when activity stream is present or unavailable.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

Introduce conditional auditing for RBAC assignment models by adding a module-level _AuditableBase that becomes AuditableModel when ansible_base.activitystream is installed; update AssignmentBase to inherit it and add audit_log_enabled = True and activity_stream_enabled = False. Add tests verifying audit events and absence of ActivityStream entries.

Changes

Cohort / File(s) Summary
Model Audit Configuration
ansible_base/rbac/models/role.py
Add _AuditableBase = object default and conditional assignment to AuditableModel when 'ansible_base.activitystream' in settings.INSTALLED_APPS; update AssignmentBase class signature to inherit _AuditableBase; add public flags audit_log_enabled = True and activity_stream_enabled = False.
Audit Logging Tests
test_app/tests/rbac/models/test_assignment_audit_logging.py
Add new tests asserting audit_log_enabled is True on RoleUserAssignment/RoleTeamAssignment, activity_stream_enabled is False (skipped if activitystream absent), patch log_auth_event to verify create/delete audit messages, and assert no ActivityStream Entry objects are created when activitystream is installed.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~15 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically indicates the main change: adding AuditableModel support to Role*Assignment classes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link
Contributor

@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.

🧹 Nitpick comments (2)
test_app/tests/rbac/models/test_assignment_audit_logging.py (1)

29-54: Attribute-only tests marked django_db and unnecessarily skipped when activitystream is absent.

Two issues:

  1. Lines 29–54 read only class-level attributes (getattr(RoleUserAssignment, ...)) — no DB interaction occurs, so @pytest.mark.django_db adds overhead for no reason.
  2. The skipif(not ACTIVITYSTREAM_INSTALLED, ...) guard is misleading: audit_log_enabled = True and activity_stream_enabled = False are assigned unconditionally on AssignmentBase (role.py lines 438–440) and don't depend on activitystream being present. Skipping when it's absent creates a false coverage gap.
✏️ Suggested fix
-@pytest.mark.django_db
-@pytest.mark.skipif(not ACTIVITYSTREAM_INSTALLED, reason="activitystream not installed")
 def test_role_user_assignment_has_audit_log_enabled():
     """RoleUserAssignment has audit_log_enabled = True to enable audit logging."""
     assert getattr(RoleUserAssignment, "audit_log_enabled", False) is True


-@pytest.mark.django_db
-@pytest.mark.skipif(not ACTIVITYSTREAM_INSTALLED, reason="activitystream not installed")
 def test_role_user_assignment_has_activity_stream_disabled():
     """RoleUserAssignment has activity_stream_enabled = False to disable activity stream."""
     assert getattr(RoleUserAssignment, "activity_stream_enabled", True) is False


-@pytest.mark.django_db
-@pytest.mark.skipif(not ACTIVITYSTREAM_INSTALLED, reason="activitystream not installed")
 def test_role_team_assignment_has_audit_log_enabled():
     """RoleTeamAssignment has audit_log_enabled = True to enable audit logging."""
     assert getattr(RoleTeamAssignment, "audit_log_enabled", False) is True


-@pytest.mark.django_db
-@pytest.mark.skipif(not ACTIVITYSTREAM_INSTALLED, reason="activitystream not installed")
 def test_role_team_assignment_has_activity_stream_disabled():
     """RoleTeamAssignment has activity_stream_enabled = False to disable activity stream."""
     assert getattr(RoleTeamAssignment, "activity_stream_enabled", True) is False
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test_app/tests/rbac/models/test_assignment_audit_logging.py` around lines 29
- 54, These tests only read class-level attributes
(RoleUserAssignment.audit_log_enabled,
RoleUserAssignment.activity_stream_enabled,
RoleTeamAssignment.audit_log_enabled,
RoleTeamAssignment.activity_stream_enabled) and do not touch the DB or depend on
the activitystream package; remove the `@pytest.mark.django_db` and
`@pytest.mark.skipif`(not ACTIVITYSTREAM_INSTALLED, ...) decorators from the test
functions so they run unconditionally and without unnecessary DB setup, keeping
the existing getattr assertions intact; you can reference AssignmentBase where
those attributes are defined but no runtime change is needed beyond removing the
two decorators from the four test functions.
ansible_base/rbac/models/role.py (1)

437-440: Misleading comment — audit_log_enabled is set unconditionally, not conditionally.

The comment # Enable audit log for this model (only if activitystream is installed) is inaccurate. Both flags are set as unconditional class attributes on AssignmentBase regardless of whether ansible_base.activitystream is in INSTALLED_APPS. They're harmless when _AuditableBase = object (no signal handler consumes them), but the comment implies conditional behavior that doesn't exist.

✏️ Suggested fix
-    # Enable audit log for this model (only if activitystream is installed)
-    audit_log_enabled = True
-    # Disable activity stream for this model
-    activity_stream_enabled = False
+    # When activitystream is installed (_AuditableBase = AuditableModel), these flags
+    # override AuditableModel's defaults to enable audit logging and suppress activity stream entries.
+    audit_log_enabled = True
+    activity_stream_enabled = False
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ansible_base/rbac/models/role.py` around lines 437 - 440, The comment on
AssignmentBase is misleading because audit_log_enabled and
activity_stream_enabled are set unconditionally; update the comment near the
class attributes audit_log_enabled and activity_stream_enabled to state they are
unconditional class attributes (harmless when ansible_base.activitystream isn't
installed) or remove the parenthetical about "only if activitystream is
installed" so it accurately reflects that these flags are always present; locate
the attributes audit_log_enabled and activity_stream_enabled in the
AssignmentBase definition and edit the comment accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ansible_base/rbac/models/role.py`:
- Around line 437-440: The comment on AssignmentBase is misleading because
audit_log_enabled and activity_stream_enabled are set unconditionally; update
the comment near the class attributes audit_log_enabled and
activity_stream_enabled to state they are unconditional class attributes
(harmless when ansible_base.activitystream isn't installed) or remove the
parenthetical about "only if activitystream is installed" so it accurately
reflects that these flags are always present; locate the attributes
audit_log_enabled and activity_stream_enabled in the AssignmentBase definition
and edit the comment accordingly.

In `@test_app/tests/rbac/models/test_assignment_audit_logging.py`:
- Around line 29-54: These tests only read class-level attributes
(RoleUserAssignment.audit_log_enabled,
RoleUserAssignment.activity_stream_enabled,
RoleTeamAssignment.audit_log_enabled,
RoleTeamAssignment.activity_stream_enabled) and do not touch the DB or depend on
the activitystream package; remove the `@pytest.mark.django_db` and
`@pytest.mark.skipif`(not ACTIVITYSTREAM_INSTALLED, ...) decorators from the test
functions so they run unconditionally and without unnecessary DB setup, keeping
the existing getattr assertions intact; you can reference AssignmentBase where
those attributes are defined but no runtime change is needed beyond removing the
two decorators from the four test functions.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 3a5f2b6 and 439ef83.

📒 Files selected for processing (2)
  • ansible_base/rbac/models/role.py
  • test_app/tests/rbac/models/test_assignment_audit_logging.py

@sonarqubecloud
Copy link

@AlanCoding
Copy link
Member

This is different from #945, where all of the components are using the RBAC app.

I know this is a lost cause, but we ideally shouldn't be referencing settings on import. This makes the entire class structure dynamic, which is a dramatic level of debt.

@huffmanca
Copy link
Contributor Author

huffmanca commented Feb 26, 2026

@AlanCoding ,

Since it's used by all components, then I'm assuming we can safely drop the conditional import check. Once that's done, is there a better way to meet the current requirement/proposal for having these audit logs (but no activity stream) for RoleUserAssignment and RoleTeamAssignment?

I don't mind reworking the PR, but since we're moving forward (at least for immediacy + backporting to 2.5 and 2.6) with the logging controlled by AuditableModel, that's the direction I'd like to use for now. Then fix it in 2.7 with something like 951 .

@jay-steurer
Copy link
Contributor

Comprehensive PR Review Summary

Thank you for this well-structured implementation of audit logging for role assignments! The core functionality is solid and follows good Django patterns. However, I've identified several critical issues that should be addressed before merging, particularly around error handling and silent failure modes.

🚨 Critical Issues (Must Fix)

1. Silent Security Degradation

The conditional inheritance pattern silently disables audit logging when activitystream is not installed, without any warnings. This could cause compliance violations in production.

Risk: Operators may believe audit logging is active when it's completely disabled.

2. Unprotected Signal Handlers

Audit logging failures will crash database transactions instead of being handled gracefully.

3. Missing Deletion Tests

No tests verify audit logs are created when role assignments are deleted (the primary mutation after creation).

📋 Required Actions

Before Merge:

  • Add error handling to prevent audit failures from killing transactions
  • Either fail loudly when activitystream missing OR log prominent warnings
  • Add tests for assignment deletion scenarios
  • Fix misleading audit_log_enabled = True when logging is non-functional

Strongly Recommended:

  • Add cascade deletion audit tests (user/team/resource deletion)
  • Enhance test assertions to verify logged data accuracy
  • Address code style issues (spacing, constants, type annotations)

🌟 Strengths

I'll add specific line-by-line comments with detailed recommendations. The security aspects are the highest priority - this feature must fail safely rather than silently.

Great work overall! The implementation approach is sound, it just needs stronger defensive programming.

huffmanca and others added 2 commits February 26, 2026 14:34
Adds AuditableModel to AssignmentBase so that it can be
leveraged by RoleUserAssignment and RoleTeamAssignment.
This is configured to have audit logs created for these
classes without updating the ActivityStream.

Co-authored-by: Claude <noreply@anthropic.com>
Includes tests to confirm that audit log events
are generated when Role*Assignment entries are
deleted.

Co-authored-by: Claude <noreply@anthropic.com>
@github-actions
Copy link

DVCS PR Check Results:

PR appears valid (JIRA key(s) found)

Copy link
Contributor

@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: 1

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

Inline comments:
In `@ansible_base/rbac/models/role.py`:
- Around line 34-39: The current fallback to _AuditableBase = object silently
disables auditing; update the module so that when 'ansible_base.activitystream'
is not in settings.INSTALLED_APPS you either log a prominent warning or raise an
error instead of silently continuing. Specifically, after the fallback
assignment of _AuditableBase, import logging (or
django.core.exceptions.ImproperlyConfigured) and if the fallback is used emit a
clear logger.warning (or raise ImproperlyConfigured) stating that AuditableModel
is missing and audit logging on AssignmentBase (and models using
audit_log_enabled) will be disabled; reference _AuditableBase, AuditableModel
and AssignmentBase in the message so the location and impact are obvious.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 439ef83 and 679a65d.

📒 Files selected for processing (2)
  • ansible_base/rbac/models/role.py
  • test_app/tests/rbac/models/test_assignment_audit_logging.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • test_app/tests/rbac/models/test_assignment_audit_logging.py

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.

3 participants