mirror of https://github.com/zulip/zulip.git
Add RealmAuditLog table and record user activation/deactivation events.
The RealmAuditLog will make it easier for server admins to replay history.
This commit is contained in:
parent
3fdad8b64a
commit
51b7677db7
|
@ -11,7 +11,8 @@ from analytics.lib.time_utils import time_range
|
|||
from analytics.models import BaseCount, InstallationCount, RealmCount, \
|
||||
UserCount, StreamCount, FillState
|
||||
from zerver.lib.timestamp import floor_to_day
|
||||
from zerver.models import Realm, UserProfile, Stream, Message, Client
|
||||
from zerver.models import Realm, UserProfile, Stream, Message, Client, \
|
||||
RealmAuditLog
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
@ -26,10 +27,14 @@ class Command(BaseCommand):
|
|||
|
||||
def create_user(self, email, full_name, is_staff, date_joined, realm):
|
||||
# type: (Text, Text, Text, bool, datetime, Realm) -> UserProfile
|
||||
return UserProfile.objects.create(
|
||||
user = UserProfile.objects.create(
|
||||
email=email, full_name=full_name, is_staff=is_staff,
|
||||
realm=realm, short_name=full_name, pointer=-1, last_pointer_updater='none',
|
||||
api_key='42', date_joined=date_joined)
|
||||
RealmAuditLog.objects.create(
|
||||
realm=realm, modified_user=user, event_type='user_created',
|
||||
event_time=user.date_joined)
|
||||
return user
|
||||
|
||||
def generate_fixture_data(self, stat, business_hours_base, non_business_hours_base,
|
||||
growth, autocorrelation, spikiness, holiday_rate=0):
|
||||
|
|
|
@ -27,7 +27,7 @@ from zerver.lib.message import (
|
|||
)
|
||||
from zerver.lib.realm_icon import realm_icon_url
|
||||
from zerver.models import Realm, RealmEmoji, Stream, UserProfile, UserActivity, RealmAlias, \
|
||||
Subscription, Recipient, Message, Attachment, UserMessage, \
|
||||
Subscription, Recipient, Message, Attachment, UserMessage, RealmAuditLog, \
|
||||
Client, DefaultStream, UserPresence, Referral, PushDeviceToken, MAX_SUBJECT_LENGTH, \
|
||||
MAX_MESSAGE_LENGTH, get_client, get_stream, get_recipient, get_huddle, \
|
||||
get_user_profile_by_id, PreregistrationUser, get_display_recipient, \
|
||||
|
@ -399,8 +399,19 @@ def do_create_user(email, password, realm, full_name, short_name,
|
|||
default_all_public_streams=None, prereg_user=None,
|
||||
newsletter_data=None):
|
||||
# type: (Text, Text, Realm, Text, Text, bool, Optional[int], Optional[UserProfile], Optional[Text], Text, Optional[Stream], Optional[Stream], bool, Optional[PreregistrationUser], Optional[Dict[str, str]]) -> UserProfile
|
||||
user_profile = create_user(email=email, password=password, realm=realm,
|
||||
full_name=full_name, short_name=short_name,
|
||||
active=active, bot_type=bot_type, bot_owner=bot_owner,
|
||||
tos_version=tos_version, avatar_source=avatar_source,
|
||||
default_sending_stream=default_sending_stream,
|
||||
default_events_register_stream=default_events_register_stream,
|
||||
default_all_public_streams=default_all_public_streams)
|
||||
|
||||
RealmAuditLog.objects.create(realm=user_profile.realm, modified_user=user_profile,
|
||||
event_type='user_created', event_time=user_profile.date_joined)
|
||||
|
||||
event = {'type': 'user_created',
|
||||
'timestamp': time.time(),
|
||||
'timestamp': datetime_to_timestamp(user_profile.date_joined),
|
||||
'full_name': full_name,
|
||||
'short_name': short_name,
|
||||
'user': email,
|
||||
|
@ -410,14 +421,6 @@ def do_create_user(email, password, realm, full_name, short_name,
|
|||
event['bot_owner'] = bot_owner.email
|
||||
log_event(event)
|
||||
|
||||
user_profile = create_user(email=email, password=password, realm=realm,
|
||||
full_name=full_name, short_name=short_name,
|
||||
active=active, bot_type=bot_type, bot_owner=bot_owner,
|
||||
tos_version=tos_version, avatar_source=avatar_source,
|
||||
default_sending_stream=default_sending_stream,
|
||||
default_events_register_stream=default_events_register_stream,
|
||||
default_all_public_streams=default_all_public_streams)
|
||||
|
||||
notify_created_user(user_profile)
|
||||
if bot_type:
|
||||
notify_created_bot(user_profile)
|
||||
|
@ -629,9 +632,13 @@ def do_deactivate_user(user_profile, log=True, _cascade=True):
|
|||
|
||||
delete_user_sessions(user_profile)
|
||||
|
||||
event_time = timezone.now()
|
||||
RealmAuditLog.objects.create(realm=user_profile.realm, modified_user=user_profile,
|
||||
event_type='user_deactivated', event_time=event_time)
|
||||
|
||||
if log:
|
||||
log_event({'type': 'user_deactivated',
|
||||
'timestamp': time.time(),
|
||||
'timestamp': datetime_to_timestamp(event_time),
|
||||
'user': user_profile.email,
|
||||
'domain': user_profile.realm.domain})
|
||||
|
||||
|
@ -1846,9 +1853,14 @@ def do_activate_user(user_profile, log=True, join_date=timezone.now()):
|
|||
user_profile.save(update_fields=["is_active", "date_joined", "password",
|
||||
"is_mirror_dummy", "tos_version"])
|
||||
|
||||
event_time = timezone.now()
|
||||
RealmAuditLog.objects.create(realm=user_profile.realm, modified_user=user_profile,
|
||||
event_type='user_activated', event_time=event_time)
|
||||
|
||||
if log:
|
||||
domain = user_profile.realm.domain
|
||||
log_event({'type': 'user_activated',
|
||||
'timestamp': datetime_to_timestamp(event_time),
|
||||
'user': user_profile.email,
|
||||
'domain': domain})
|
||||
|
||||
|
@ -1861,8 +1873,13 @@ def do_reactivate_user(user_profile):
|
|||
user_profile.is_active = True
|
||||
user_profile.save(update_fields=["is_active"])
|
||||
|
||||
event_time = timezone.now()
|
||||
RealmAuditLog.objects.create(realm=user_profile.realm, modified_user=user_profile,
|
||||
event_type='user_reactivated', event_time=event_time)
|
||||
|
||||
domain = user_profile.realm.domain
|
||||
log_event({'type': 'user_reactivated',
|
||||
'timestamp': datetime_to_timestamp(event_time),
|
||||
'user': user_profile.email,
|
||||
'domain': domain})
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import Any, Dict, Iterable, List, Mapping, Optional, Set, Tuple, Tex
|
|||
|
||||
from zerver.lib.initial_password import initial_password
|
||||
from zerver.models import Realm, Stream, UserProfile, Huddle, \
|
||||
Subscription, Recipient, Client, get_huddle_hash
|
||||
Subscription, Recipient, Client, RealmAuditLog, get_huddle_hash
|
||||
from zerver.lib.create_user import create_user_profile
|
||||
|
||||
def bulk_create_realms(realm_list):
|
||||
|
@ -35,6 +35,11 @@ def bulk_create_users(realm, users_raw, bot_type=None, tos_version=None):
|
|||
profiles_to_create.append(profile)
|
||||
UserProfile.objects.bulk_create(profiles_to_create)
|
||||
|
||||
RealmAuditLog.objects.bulk_create(
|
||||
[RealmAuditLog(realm=profile_.realm, modified_user=profile_,
|
||||
event_type='user_created', event_time=profile_.date_joined)
|
||||
for profile_ in profiles_to_create])
|
||||
|
||||
profiles_by_email = {} # type: Dict[Text, UserProfile]
|
||||
profiles_by_id = {} # type: Dict[int, UserProfile]
|
||||
for profile in UserProfile.objects.select_related().all():
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-03-04 07:33
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db.backends.postgresql_psycopg2.schema import DatabaseSchemaEditor
|
||||
from django.db.migrations.state import StateApps
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from zerver.models import RealmAuditLog, UserProfile
|
||||
|
||||
def backfill_user_activations_and_deactivations(apps, schema_editor):
|
||||
# type: (StateApps, DatabaseSchemaEditor) -> None
|
||||
migration_time = timezone.now()
|
||||
|
||||
for user in UserProfile.objects.all():
|
||||
RealmAuditLog.objects.create(realm=user.realm, modified_user=user,
|
||||
event_type='user_created', event_time=user.date_joined,
|
||||
backfilled=False)
|
||||
|
||||
for user in UserProfile.objects.filter(is_active=False):
|
||||
RealmAuditLog.objects.create(realm=user.realm, modified_user=user,
|
||||
event_type='user_deactivated', event_time=migration_time,
|
||||
backfilled=True)
|
||||
|
||||
def reverse_code(apps, schema_editor):
|
||||
# type: (StateApps, DatabaseSchemaEditor) -> None
|
||||
RealmAuditLog.objects.filter(event_type='user_created').delete()
|
||||
RealmAuditLog.objects.filter(event_type='user_deactivated').delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('zerver', '0056_userprofile_emoji_alt_code'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='RealmAuditLog',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('event_type', models.CharField(max_length=40)),
|
||||
('backfilled', models.BooleanField(default=False)),
|
||||
('event_time', models.DateTimeField()),
|
||||
('acting_user', models.ForeignKey(null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='+',
|
||||
to=settings.AUTH_USER_MODEL)),
|
||||
('modified_stream', models.ForeignKey(null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to='zerver.Stream')),
|
||||
('modified_user', models.ForeignKey(null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name='+',
|
||||
to=settings.AUTH_USER_MODEL)),
|
||||
('realm', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='zerver.Realm')),
|
||||
],
|
||||
),
|
||||
|
||||
migrations.RunPython(backfill_user_activations_and_deactivations,
|
||||
reverse_code=reverse_code),
|
||||
|
||||
]
|
|
@ -1485,3 +1485,12 @@ class ScheduledJob(models.Model):
|
|||
# Kind if like a ForeignKey, but table is determined by type.
|
||||
filter_id = models.IntegerField(null=True) # type: Optional[int]
|
||||
filter_string = models.CharField(max_length=100) # type: Text
|
||||
|
||||
class RealmAuditLog(models.Model):
|
||||
realm = models.ForeignKey(Realm) # type: Realm
|
||||
acting_user = models.ForeignKey(UserProfile, null=True, related_name='+') # type: Optional[UserProfile]
|
||||
modified_user = models.ForeignKey(UserProfile, null=True, related_name='+') # type: Optional[UserProfile]
|
||||
modified_stream = models.ForeignKey(Stream, null=True) # type: Optional[Stream]
|
||||
event_type = models.CharField(max_length=40) # type: Text
|
||||
event_time = models.DateTimeField() # type: datetime.datetime
|
||||
backfilled = models.BooleanField(default=False) # type: bool
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
from django.utils import timezone
|
||||
|
||||
from zerver.lib.actions import do_create_user, do_deactivate_user, \
|
||||
do_activate_user, do_reactivate_user
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.models import RealmAuditLog, get_realm
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
class TestUserActivation(ZulipTestCase):
|
||||
def test_user_activation(self):
|
||||
# type: () -> None
|
||||
realm = get_realm('zulip')
|
||||
now = timezone.now()
|
||||
user = do_create_user('email', 'password', realm, 'full_name', 'short_name')
|
||||
do_deactivate_user(user)
|
||||
do_activate_user(user)
|
||||
do_deactivate_user(user)
|
||||
do_reactivate_user(user)
|
||||
self.assertEqual(RealmAuditLog.objects.filter(event_time__gte=now).count(), 5)
|
||||
event_types = list(RealmAuditLog.objects.filter(
|
||||
realm=realm, acting_user=None, modified_user=user, modified_stream=None,
|
||||
event_time__gte=now, event_time__lte=now+timedelta(minutes=60))
|
||||
.order_by('event_time').values_list('event_type', flat=True))
|
||||
self.assertEqual(event_types, ['user_created', 'user_deactivated', 'user_activated',
|
||||
'user_deactivated', 'user_reactivated'])
|
Loading…
Reference in New Issue