mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 18:21:12 +03:00
Merge pull request #1771 from wwitzel3/issue-1608
Implement ActivityStream for RBAC Roles
This commit is contained in:
commit
5291420a17
@ -90,6 +90,7 @@ SUMMARIZABLE_FK_FIELDS = {
|
|||||||
'current_job': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'),
|
'current_job': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'license_error'),
|
||||||
'inventory_source': ('source', 'last_updated', 'status'),
|
'inventory_source': ('source', 'last_updated', 'status'),
|
||||||
'source_script': ('name', 'description'),
|
'source_script': ('name', 'description'),
|
||||||
|
'role': ('id', 'role_field')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
19
awx/main/migrations/0021_v300_activity_stream.py
Normal file
19
awx/main/migrations/0021_v300_activity_stream.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0020_v300_labels_changes'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='activitystream',
|
||||||
|
name='role',
|
||||||
|
field=models.ManyToManyField(to='main.Role', blank=True),
|
||||||
|
),
|
||||||
|
]
|
@ -56,6 +56,7 @@ class ActivityStream(models.Model):
|
|||||||
notifier = models.ManyToManyField("Notifier", blank=True)
|
notifier = models.ManyToManyField("Notifier", blank=True)
|
||||||
notification = models.ManyToManyField("Notification", blank=True)
|
notification = models.ManyToManyField("Notification", blank=True)
|
||||||
label = models.ManyToManyField("Label", blank=True)
|
label = models.ManyToManyField("Label", blank=True)
|
||||||
|
role = models.ManyToManyField("Role", blank=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('api:activity_stream_detail', args=(self.pk,))
|
return reverse('api:activity_stream_detail', args=(self.pk,))
|
||||||
|
@ -156,6 +156,20 @@ def org_admin_edit_members(instance, action, model, reverse, pk_set, **kwargs):
|
|||||||
if action == 'pre_remove':
|
if action == 'pre_remove':
|
||||||
instance.content_object.admin_role.children.remove(user.admin_role)
|
instance.content_object.admin_role.children.remove(user.admin_role)
|
||||||
|
|
||||||
|
def rbac_activity_stream(instance, sender, **kwargs):
|
||||||
|
user_type = ContentType.objects.get_for_model(User)
|
||||||
|
# Only if we are associating/disassociating
|
||||||
|
if kwargs['action'] in ['pre_add', 'pre_remove']:
|
||||||
|
# Only if this isn't for the User.admin_role
|
||||||
|
if hasattr(instance, 'content_type'):
|
||||||
|
if instance.content_type in [None, user_type]:
|
||||||
|
return
|
||||||
|
role = instance
|
||||||
|
instance = instance.content_object
|
||||||
|
else:
|
||||||
|
role = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']).first()
|
||||||
|
activity_stream_associate(sender, instance, role=role, **kwargs)
|
||||||
|
|
||||||
def cleanup_detached_labels_on_deleted_parent(sender, instance, **kwargs):
|
def cleanup_detached_labels_on_deleted_parent(sender, instance, **kwargs):
|
||||||
for l in instance.labels.all():
|
for l in instance.labels.all():
|
||||||
if l.is_candidate_for_detach():
|
if l.is_candidate_for_detach():
|
||||||
@ -177,6 +191,7 @@ post_save.connect(emit_job_event_detail, sender=JobEvent)
|
|||||||
post_save.connect(emit_ad_hoc_command_event_detail, sender=AdHocCommandEvent)
|
post_save.connect(emit_ad_hoc_command_event_detail, sender=AdHocCommandEvent)
|
||||||
m2m_changed.connect(rebuild_role_ancestor_list, Role.parents.through)
|
m2m_changed.connect(rebuild_role_ancestor_list, Role.parents.through)
|
||||||
m2m_changed.connect(org_admin_edit_members, Role.members.through)
|
m2m_changed.connect(org_admin_edit_members, Role.members.through)
|
||||||
|
m2m_changed.connect(rbac_activity_stream, Role.members.through)
|
||||||
post_save.connect(sync_superuser_status_to_rbac, sender=User)
|
post_save.connect(sync_superuser_status_to_rbac, sender=User)
|
||||||
post_save.connect(create_user_role, sender=User)
|
post_save.connect(create_user_role, sender=User)
|
||||||
pre_delete.connect(cleanup_detached_labels_on_deleted_parent, sender=UnifiedJob)
|
pre_delete.connect(cleanup_detached_labels_on_deleted_parent, sender=UnifiedJob)
|
||||||
@ -354,7 +369,7 @@ def activity_stream_delete(sender, instance, **kwargs):
|
|||||||
def activity_stream_associate(sender, instance, **kwargs):
|
def activity_stream_associate(sender, instance, **kwargs):
|
||||||
if not activity_stream_enabled:
|
if not activity_stream_enabled:
|
||||||
return
|
return
|
||||||
if 'pre_add' in kwargs['action'] or 'pre_remove' in kwargs['action']:
|
if kwargs['action'] in ['pre_add', 'pre_remove']:
|
||||||
if kwargs['action'] == 'pre_add':
|
if kwargs['action'] == 'pre_add':
|
||||||
action = 'associate'
|
action = 'associate'
|
||||||
elif kwargs['action'] == 'pre_remove':
|
elif kwargs['action'] == 'pre_remove':
|
||||||
@ -383,6 +398,23 @@ def activity_stream_associate(sender, instance, **kwargs):
|
|||||||
getattr(activity_entry, object1).add(obj1)
|
getattr(activity_entry, object1).add(obj1)
|
||||||
getattr(activity_entry, object2).add(obj2_actual)
|
getattr(activity_entry, object2).add(obj2_actual)
|
||||||
|
|
||||||
|
# Record the role for RBAC changes
|
||||||
|
if 'role' in kwargs:
|
||||||
|
role = kwargs['role']
|
||||||
|
if role.content_object is not None:
|
||||||
|
obj_rel = '.'.join([role.content_object.__module__,
|
||||||
|
role.content_object.__class__.__name__,
|
||||||
|
role.role_field])
|
||||||
|
|
||||||
|
# If the m2m is from the User side we need to
|
||||||
|
# set the content_object of the Role for our entry.
|
||||||
|
if type(instance) == User and role.content_object is not None:
|
||||||
|
getattr(activity_entry, role.content_type.name).add(role.content_object)
|
||||||
|
|
||||||
|
activity_entry.role.add(role)
|
||||||
|
activity_entry.object_relationship_type = obj_rel
|
||||||
|
activity_entry.save()
|
||||||
|
|
||||||
|
|
||||||
@receiver(current_user_getter)
|
@receiver(current_user_getter)
|
||||||
def get_current_user_from_drf_request(sender, **kwargs):
|
def get_current_user_from_drf_request(sender, **kwargs):
|
||||||
|
@ -59,3 +59,29 @@ def test_middleware_actor_added(monkeypatch, post, get, user):
|
|||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert response.data['summary_fields']['actor']['username'] == 'admin-poster'
|
assert response.data['summary_fields']['actor']['username'] == 'admin-poster'
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not getattr(settings, 'ACTIVITY_STREAM_ENABLED', True), reason="Activity stream not enabled")
|
||||||
|
@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_rbac_stream_resource_roles(mocker, organization, user):
|
||||||
|
member = user('test', False)
|
||||||
|
organization.admin_role.members.add(member)
|
||||||
|
|
||||||
|
activity_stream = ActivityStream.objects.filter(organization__pk=organization.pk, operation='associate').first()
|
||||||
|
assert activity_stream.user.first() == member
|
||||||
|
assert activity_stream.organization.first() == organization
|
||||||
|
assert activity_stream.role.first() == organization.admin_role
|
||||||
|
assert activity_stream.object_relationship_type == 'awx.main.models.organization.Organization.admin_role'
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not getattr(settings, 'ACTIVITY_STREAM_ENABLED', True), reason="Activity stream not enabled")
|
||||||
|
@mock.patch('awx.api.views.feature_enabled', new=mock_feature_enabled)
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_rbac_stream_user_roles(mocker, organization, user):
|
||||||
|
member = user('test', False)
|
||||||
|
member.roles.add(organization.admin_role)
|
||||||
|
|
||||||
|
activity_stream = ActivityStream.objects.filter(organization__pk=organization.pk, operation='associate').first()
|
||||||
|
assert activity_stream.user.first() == member
|
||||||
|
assert activity_stream.organization.first() == organization
|
||||||
|
assert activity_stream.role.first() == organization.admin_role
|
||||||
|
assert activity_stream.object_relationship_type == 'awx.main.models.organization.Organization.admin_role'
|
||||||
|
Loading…
Reference in New Issue
Block a user