1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 08:21:15 +03:00

store denormalized metadata about ActivityStream.actor for accounting

see: https://github.com/ansible/tower/issues/1782
This commit is contained in:
Ryan Petrello 2018-05-21 16:16:18 -04:00
parent ffdd7f162b
commit f434196bae
5 changed files with 72 additions and 2 deletions

View File

@ -4831,6 +4831,9 @@ class ActivityStreamSerializer(BaseSerializer):
username = obj.actor.username,
first_name = obj.actor.first_name,
last_name = obj.actor.last_name)
elif obj.deleted_actor:
summary_fields['actor'] = obj.deleted_actor.copy()
summary_fields['actor']['id'] = None
if obj.setting:
summary_fields['setting'] = [obj.setting]
return summary_fields

View File

@ -0,0 +1,24 @@
#d -*- coding: utf-8 -*-
# Generated by Django 1.11.11 on 2018-05-21 19:51
from __future__ import unicode_literals
import awx.main.fields
import awx.main.models.activity_stream
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('main', '0037_v330_remove_legacy_fact_cleanup'),
]
operations = [
migrations.AddField(
model_name='activitystream',
name='deleted_actor',
field=awx.main.fields.JSONField(null=True),
),
]

View File

@ -33,6 +33,7 @@ class ActivityStream(models.Model):
operation = models.CharField(max_length=13, choices=OPERATION_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True)
changes = models.TextField(blank=True)
deleted_actor = JSONField(null=True)
object_relationship_type = models.TextField(blank=True)
object1 = models.TextField()
@ -77,6 +78,18 @@ class ActivityStream(models.Model):
return reverse('api:activity_stream_detail', kwargs={'pk': self.pk}, request=request)
def save(self, *args, **kwargs):
# Store denormalized actor metadata so that we retain it for accounting
# purposes when the User row is deleted.
if self.actor:
self.deleted_actor = {
'id': self.actor_id,
'username': self.actor.username,
'first_name': self.actor.first_name,
'last_name': self.actor.last_name,
}
if 'update_fields' in kwargs and 'deleted_actor' not in kwargs['update_fields']:
kwargs['update_fields'].append('deleted_actor')
# For compatibility with Django 1.4.x, attempt to handle any calls to
# save that pass update_fields.
try:

View File

@ -184,6 +184,32 @@ def test_annon_user_action():
assert not entry.actor
@pytest.mark.django_db
def test_activity_stream_deleted_actor(alice, bob):
alice.first_name = 'Alice'
alice.last_name = 'Doe'
alice.save()
with impersonate(alice):
o = Organization.objects.create(name='test organization')
entry = o.activitystream_set.get(operation='create')
assert entry.actor == alice
alice.delete()
entry = o.activitystream_set.get(operation='create')
assert entry.actor is None
deleted = entry.deleted_actor
assert deleted['username'] == 'alice'
assert deleted['first_name'] == 'Alice'
assert deleted['last_name'] == 'Doe'
entry.actor = bob
entry.save(update_fields=['actor'])
deleted = entry.deleted_actor
entry = ActivityStream.objects.get(id=entry.pk)
assert entry.deleted_actor['username'] == 'bob'
@pytest.mark.django_db
def test_modified_not_allowed_field(somecloud_type):
'''

View File

@ -53,8 +53,12 @@ export default ['$scope', '$state', 'subTitle', 'GetTargetTitle',
$scope.activities.forEach(function(activity, i) {
// build activity.user
if ($scope.activities[i].summary_fields.actor) {
$scope.activities[i].user = "<a href=\"/#/users/" + $scope.activities[i].summary_fields.actor.id + "\">" +
$scope.activities[i].summary_fields.actor.username + "</a>";
if ($scope.activities[i].summary_fields.actor.id) {
$scope.activities[i].user = "<a href=\"/#/users/" + $scope.activities[i].summary_fields.actor.id + "\">" +
$scope.activities[i].summary_fields.actor.username + "</a>";
} else {
$scope.activities[i].user = $scope.activities[i].summary_fields.actor.username + ' (deleted)';
}
} else {
$scope.activities[i].user = 'system';
}