Skip to content

Commit

Permalink
Merge pull request #251 from Cadasta/feature/audit-logging
Browse files Browse the repository at this point in the history
Add audit logging using simple-history package
  • Loading branch information
ian-ross authored Jun 18, 2016
2 parents e7ec98c + 59fb9fb commit 8c3bd95
Show file tree
Hide file tree
Showing 15 changed files with 494 additions and 15 deletions.
30 changes: 29 additions & 1 deletion cadasta/accounts/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-05-04 06:50
# Generated by Django 1.9.6 on 2016-06-17 19:46
from __future__ import unicode_literals

import accounts.manager
import accounts.models
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


Expand Down Expand Up @@ -44,4 +46,30 @@ class Migration(migrations.Migration):
('objects', accounts.manager.UserManager()),
],
),
migrations.CreateModel(
name='HistoricalUser',
fields=[
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(db_index=True, error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], verbose_name='username')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('full_name', models.CharField(blank=True, max_length=130, verbose_name='full name')),
('email_verified', models.BooleanField(default=False)),
('verify_email_by', models.DateTimeField(default=accounts.models.now_plus_48_hours)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'historical user',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
},
),
]
4 changes: 3 additions & 1 deletion cadasta/accounts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from tutelary.models import Policy
from tutelary.decorators import permissioned_model


from simple_history.models import HistoricalRecords
from .manager import UserManager


Expand Down Expand Up @@ -38,6 +38,8 @@ class User(auth_base.AbstractBaseUser, auth.PermissionsMixin):

objects = UserManager()

history = HistoricalRecords()

USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email', 'full_name']

Expand Down
2 changes: 2 additions & 0 deletions cadasta/config/settings/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
'allauth.account',
'allauth.socialaccount',
'sass_processor',
'simple_history',
)

MIDDLEWARE_CLASSES = (
Expand All @@ -80,6 +81,7 @@
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'audit_log.middleware.UserLoggingMiddleware',
'simple_history.middleware.HistoryRequestMiddleware'
)

REST_FRAMEWORK = {
Expand Down
2 changes: 1 addition & 1 deletion cadasta/geography/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-05-04 06:50
# Generated by Django 1.9.6 on 2016-06-17 19:46
from __future__ import unicode_literals

import django.contrib.gis.db.models.fields
Expand Down
109 changes: 108 additions & 1 deletion cadasta/organization/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-05-31 15:43
# Generated by Django 1.9.6 on 2016-06-17 19:46
from __future__ import unicode_literals

import core.models
Expand All @@ -23,6 +23,88 @@ class Migration(migrations.Migration):
]

operations = [
migrations.CreateModel(
name='HistoricalOrganization',
fields=[
('id', models.CharField(db_index=True, max_length=24)),
('name', models.CharField(max_length=200)),
('slug', models.SlugField()),
('description', models.TextField(blank=True, null=True)),
('archived', models.BooleanField(default=False)),
('urls', django.contrib.postgres.fields.ArrayField(base_field=models.URLField(), default=[], size=None)),
('contacts', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=[], null=True, validators=[organization.validators.validate_contact])),
('logo', models.URLField(null=True)),
('last_updated', models.DateTimeField(blank=True, editable=False)),
('access', models.CharField(choices=[('public', 'Public'), ('private', 'Private')], default='public', max_length=8)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'historical organization',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
},
),
migrations.CreateModel(
name='HistoricalOrganizationRole',
fields=[
('id', models.CharField(db_index=True, max_length=24)),
('admin', models.BooleanField(default=False)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'historical organization role',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
},
),
migrations.CreateModel(
name='HistoricalProject',
fields=[
('id', models.CharField(db_index=True, max_length=24)),
('name', models.CharField(max_length=100)),
('slug', models.SlugField(null=True)),
('country', django_countries.fields.CountryField(max_length=2, null=True)),
('description', models.TextField(blank=True, null=True)),
('archived', models.BooleanField(default=False)),
('urls', django.contrib.postgres.fields.ArrayField(base_field=models.URLField(), default=[], size=None)),
('contacts', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=[], null=True, validators=[organization.validators.validate_contact])),
('last_updated', models.DateTimeField(blank=True, editable=False)),
('extent', django.contrib.gis.db.models.fields.PolygonField(null=True, srid=4326)),
('access', models.CharField(choices=[('public', 'Public'), ('private', 'Private')], default='public', max_length=8)),
('current_questionnaire', models.CharField(blank=True, max_length=24, null=True)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'historical project',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
},
),
migrations.CreateModel(
name='HistoricalProjectRole',
fields=[
('id', models.CharField(db_index=True, max_length=24)),
('role', models.CharField(choices=[('PU', 'Project User'), ('DC', 'Data Collector'), ('PM', 'Project Manager')], default='PU', max_length=2)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'historical project role',
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
},
),
migrations.CreateModel(
name='Organization',
fields=[
Expand Down Expand Up @@ -95,6 +177,31 @@ class Migration(migrations.Migration):
name='users',
field=models.ManyToManyField(related_name='organizations', through='organization.OrganizationRole', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='historicalprojectrole',
name='project',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='organization.Project'),
),
migrations.AddField(
model_name='historicalprojectrole',
name='user',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='historicalproject',
name='organization',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='organization.Organization'),
),
migrations.AddField(
model_name='historicalorganizationrole',
name='organization',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='organization.Organization'),
),
migrations.AddField(
model_name='historicalorganizationrole',
name='user',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL),
),
migrations.AlterUniqueTogether(
name='projectrole',
unique_together=set([('project', 'user')]),
Expand Down
10 changes: 9 additions & 1 deletion cadasta/organization/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.dispatch import receiver
from django.utils.translation import ugettext as _
import django.contrib.gis.db.models as gismodels

from simple_history.models import HistoricalRecords

from tutelary.decorators import permissioned_model
from tutelary.models import Policy
Expand Down Expand Up @@ -46,6 +46,8 @@ class Organization(SlugModel, RandomIDModel):
default="public", choices=ACCESS_CHOICES, max_length=8
)

history = HistoricalRecords()

class Meta:
ordering = ('name',)

Expand Down Expand Up @@ -103,6 +105,8 @@ class OrganizationRole(RandomIDModel):
user = models.ForeignKey('accounts.User')
admin = models.BooleanField(default=False)

history = HistoricalRecords()


def reassign_user_policies(instance, adding):
assigned_policies = instance.user.assigned_policies()
Expand Down Expand Up @@ -165,6 +169,8 @@ class Project(ResourceModelMixin, SlugModel, RandomIDModel):
max_length=24, null=True, blank=True
)

history = HistoricalRecords()

class Meta:
ordering = ('organization', 'name')

Expand Down Expand Up @@ -235,6 +241,8 @@ class ProjectRole(RandomIDModel):
choices=ROLE_CHOICES,
default='PU')

history = HistoricalRecords()

class Meta:
unique_together = ('project', 'user')

Expand Down
Loading

0 comments on commit 8c3bd95

Please sign in to comment.