1
|
|
# Python
|
2
|
3
|
import contextlib
|
3
|
3
|
import logging
|
4
|
3
|
import threading
|
5
|
3
|
import json
|
6
|
|
|
7
|
|
# Django
|
8
|
3
|
from django.conf import settings
|
9
|
3
|
from django.db.models.signals import post_save
|
10
|
3
|
from django.dispatch import receiver
|
11
|
|
|
12
|
|
# Django-CRUM
|
13
|
3
|
from crum import get_current_request, get_current_user
|
14
|
3
|
from crum.signals import current_user_getter
|
15
|
|
|
16
|
|
# CyBorgBackup
|
17
|
3
|
from cyborgbackup.main.models import User, JobEvent, Client, Policy, Schedule, Job, ActivityStream, Repository
|
18
|
3
|
from cyborgbackup.api.serializers import (JobEventWebSocketSerializer, JobSerializer, ClientSerializer,
|
19
|
|
RepositorySerializer, ScheduleSerializer, PolicySerializer)
|
20
|
3
|
from cyborgbackup.main.utils.common import model_instance_diff, model_to_dict, camelcase_to_underscore
|
21
|
|
|
22
|
3
|
from cyborgbackup.main import consumers
|
23
|
|
|
24
|
3
|
__all__ = []
|
25
|
|
|
26
|
3
|
logger = logging.getLogger('cyborgbackup.main.signals')
|
27
|
|
|
28
|
|
|
29
|
3
|
def get_current_user_or_none():
|
30
|
3
|
u = get_current_user()
|
31
|
3
|
if not isinstance(u, User):
|
32
|
3
|
return None
|
33
|
0
|
return u
|
34
|
|
|
35
|
|
|
36
|
3
|
def emit_event_detail(serializer, relation, **kwargs):
|
37
|
0
|
instance = kwargs['instance']
|
38
|
0
|
created = kwargs['created']
|
39
|
0
|
if created:
|
40
|
0
|
event_serializer = serializer(instance)
|
41
|
0
|
consumers.emit_channel_notification(
|
42
|
|
'-'.join([event_serializer.get_group_name(instance), str(getattr(instance, relation))]),
|
43
|
|
event_serializer.data
|
44
|
|
)
|
45
|
|
|
46
|
|
|
47
|
3
|
@receiver(post_save, sender=JobEvent)
|
48
|
|
def emit_job_event_detail(sender, **kwargs):
|
49
|
0
|
emit_event_detail(JobEventWebSocketSerializer, 'job_id', **kwargs)
|
50
|
|
|
51
|
|
|
52
|
3
|
class ActivityStreamEnabled(threading.local):
|
53
|
3
|
def __init__(self):
|
54
|
3
|
self.enabled = False
|
55
|
|
|
56
|
3
|
def __nonzero__(self):
|
57
|
0
|
return bool(self.enabled and getattr(settings, 'ACTIVITY_STREAM_ENABLED', True))
|
58
|
|
|
59
|
|
|
60
|
3
|
activity_stream_enabled = ActivityStreamEnabled()
|
61
|
|
|
62
|
|
|
63
|
3
|
@contextlib.contextmanager
|
64
|
|
def disable_activity_stream():
|
65
|
|
'''
|
66
|
|
Context manager to disable capturing activity stream changes.
|
67
|
|
'''
|
68
|
0
|
try:
|
69
|
0
|
previous_value = activity_stream_enabled.enabled
|
70
|
0
|
activity_stream_enabled.enabled = False
|
71
|
0
|
yield
|
72
|
|
finally:
|
73
|
0
|
activity_stream_enabled.enabled = previous_value
|
74
|
|
|
75
|
|
|
76
|
3
|
model_serializer_mapping = {
|
77
|
|
Job: JobSerializer,
|
78
|
|
Client: ClientSerializer,
|
79
|
|
Policy: PolicySerializer,
|
80
|
|
Repository: RepositorySerializer,
|
81
|
|
Schedule: ScheduleSerializer,
|
82
|
|
}
|
83
|
|
|
84
|
|
|
85
|
3
|
def activity_stream_create(sender, instance, created, **kwargs):
|
86
|
3
|
if created and activity_stream_enabled:
|
87
|
3
|
_type = type(instance)
|
88
|
3
|
if getattr(_type, '_deferred', False):
|
89
|
0
|
return
|
90
|
3
|
object1 = camelcase_to_underscore(instance.__class__.__name__)
|
91
|
3
|
changes = model_to_dict(instance, model_serializer_mapping)
|
92
|
|
# Special case where Job survey password variables need to be hidden
|
93
|
3
|
if type(instance) == Job:
|
94
|
0
|
if 'extra_vars' in changes:
|
95
|
0
|
changes['extra_vars'] = instance.display_extra_vars()
|
96
|
3
|
activity_entry = ActivityStream(
|
97
|
|
operation='create',
|
98
|
|
object1=object1,
|
99
|
|
changes=json.dumps(changes),
|
100
|
|
actor=get_current_user_or_none())
|
101
|
3
|
if instance._meta.model_name != 'setting': # Is not conf.Setting instance
|
102
|
3
|
activity_entry.save()
|
103
|
3
|
getattr(activity_entry, object1).add(instance)
|
104
|
|
|
105
|
|
|
106
|
3
|
def activity_stream_update(sender, instance, **kwargs):
|
107
|
3
|
if instance.id is None:
|
108
|
3
|
return
|
109
|
3
|
if not activity_stream_enabled:
|
110
|
0
|
return
|
111
|
3
|
try:
|
112
|
3
|
old = sender.objects.get(id=instance.id)
|
113
|
3
|
except sender.DoesNotExist:
|
114
|
3
|
return
|
115
|
|
|
116
|
3
|
new = instance
|
117
|
3
|
changes = model_instance_diff(old, new, model_serializer_mapping)
|
118
|
3
|
if changes is None:
|
119
|
0
|
return
|
120
|
3
|
_type = type(instance)
|
121
|
3
|
if getattr(_type, '_deferred', False):
|
122
|
0
|
return
|
123
|
3
|
object1 = camelcase_to_underscore(instance.__class__.__name__)
|
124
|
|
|
125
|
3
|
activity_entry = ActivityStream(
|
126
|
|
operation='update',
|
127
|
|
object1=object1,
|
128
|
|
changes=json.dumps(changes),
|
129
|
|
actor=get_current_user_or_none())
|
130
|
3
|
if instance._meta.model_name != 'setting': # Is not conf.Setting instance
|
131
|
3
|
activity_entry.save()
|
132
|
3
|
getattr(activity_entry, object1).add(instance)
|
133
|
|
|
134
|
|
|
135
|
3
|
def activity_stream_delete(sender, instance, **kwargs):
|
136
|
3
|
if not activity_stream_enabled:
|
137
|
0
|
return
|
138
|
3
|
_type = type(instance)
|
139
|
3
|
if getattr(_type, '_deferred', False):
|
140
|
0
|
return
|
141
|
3
|
changes = model_to_dict(instance)
|
142
|
3
|
object1 = camelcase_to_underscore(instance.__class__.__name__)
|
143
|
3
|
activity_entry = ActivityStream(
|
144
|
|
operation='delete',
|
145
|
|
changes=json.dumps(changes),
|
146
|
|
object1=object1,
|
147
|
|
actor=get_current_user_or_none())
|
148
|
3
|
activity_entry.save()
|
149
|
|
|
150
|
|
|
151
|
3
|
def activity_stream_associate(sender, instance, **kwargs):
|
152
|
3
|
if not activity_stream_enabled:
|
153
|
0
|
return
|
154
|
3
|
if kwargs['action'] in ['pre_add', 'pre_remove']:
|
155
|
3
|
if kwargs['action'] == 'pre_add':
|
156
|
3
|
action = 'associate'
|
157
|
0
|
elif kwargs['action'] == 'pre_remove':
|
158
|
0
|
action = 'disassociate'
|
159
|
|
else:
|
160
|
0
|
return
|
161
|
3
|
obj1 = instance
|
162
|
3
|
_type = type(instance)
|
163
|
3
|
if getattr(_type, '_deferred', False):
|
164
|
0
|
return
|
165
|
3
|
object1 = camelcase_to_underscore(obj1.__class__.__name__)
|
166
|
3
|
if object1 == 'activity_stream':
|
167
|
3
|
return
|
168
|
3
|
obj_rel = sender.__module__ + "." + sender.__name__
|
169
|
|
|
170
|
3
|
for entity_acted in kwargs['pk_set']:
|
171
|
3
|
obj2 = kwargs['model']
|
172
|
3
|
obj2_id = entity_acted
|
173
|
3
|
obj2_actual = obj2.objects.filter(id=obj2_id)
|
174
|
3
|
if not obj2_actual.exists():
|
175
|
0
|
continue
|
176
|
3
|
obj2_actual = obj2_actual[0]
|
177
|
3
|
_type = type(obj2_actual)
|
178
|
3
|
if getattr(_type, '_deferred', False):
|
179
|
0
|
return
|
180
|
3
|
object2 = camelcase_to_underscore(obj2.__name__)
|
181
|
3
|
activity_entry = ActivityStream(
|
182
|
|
changes=json.dumps(dict(object1=object1,
|
183
|
|
object1_pk=obj1.pk,
|
184
|
|
object2=object2,
|
185
|
|
object2_pk=obj2_id,
|
186
|
|
action=action,
|
187
|
|
relationship=obj_rel)),
|
188
|
|
operation=action,
|
189
|
|
object1=object1,
|
190
|
|
object2=object2,
|
191
|
|
object_relationship_type=obj_rel,
|
192
|
|
actor=get_current_user_or_none())
|
193
|
3
|
activity_entry.save()
|
194
|
3
|
getattr(activity_entry, object1).add(obj1)
|
195
|
3
|
getattr(activity_entry, object2).add(obj2_actual)
|
196
|
|
|
197
|
|
|
198
|
3
|
@receiver(current_user_getter)
|
199
|
|
def get_current_user_from_drf_request(sender, **kwargs):
|
200
|
|
'''
|
201
|
|
Provider a signal handler to return the current user from the current
|
202
|
|
request when using Django REST Framework. Requires that the APIView set
|
203
|
|
drf_request on the underlying Django Request object.
|
204
|
|
'''
|
205
|
3
|
request = get_current_request()
|
206
|
3
|
drf_request_user = getattr(request, 'drf_request_user', False)
|
207
|
3
|
return (drf_request_user, 0)
|
208
|
|
|
209
|
|
|
210
|
3
|
def sync_superuser_status_to_rbac(instance, **kwargs):
|
211
|
|
'When the is_superuser flag is changed on a user, reflect that in the membership of the System Admnistrator role'
|
212
|
0
|
update_fields = kwargs.get('update_fields', None)
|
213
|
0
|
if update_fields and 'is_superuser' not in update_fields:
|
214
|
0
|
return
|
215
|
|
|
216
|
|
|
217
|
|
# def create_user_role(instance, **kwargs):
|
218
|
|
# if not kwargs.get('created', True):
|
219
|
|
# return
|
220
|
|
# try:
|
221
|
|
# Role.objects.get(
|
222
|
|
# content_type=ContentType.objects.get_for_model(instance),
|
223
|
|
# object_id=instance.id,
|
224
|
|
# role_field='admin_role'
|
225
|
|
# )
|
226
|
|
# except Role.DoesNotExist:
|
227
|
|
# role = Role.objects.create(
|
228
|
|
# role_field='admin_role',
|
229
|
|
# content_object=instance,
|
230
|
|
# )
|
231
|
|
# role.members.add(instance)
|
232
|
|
|
233
|
|
|
234
|
3
|
post_save.connect(sync_superuser_status_to_rbac, sender=User)
|