Skip to content

Commit

Permalink
Add mgmt commands and tests
Browse files Browse the repository at this point in the history
Revise model migration.
Add user_password_history command.
Add management command tests.
Improve password tests.
Add Django admin functionality for new PasswordExpiry
and PasswordHistory models.
  • Loading branch information
grahamu committed Sep 13, 2016
1 parent d4d0939 commit 23edd12
Show file tree
Hide file tree
Showing 8 changed files with 407 additions and 41 deletions.
21 changes: 20 additions & 1 deletion account/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

from django.contrib import admin

from account.models import Account, SignupCode, AccountDeletion, EmailAddress
from account.models import (
Account,
AccountDeletion,
EmailAddress,
PasswordExpiry,
PasswordHistory,
SignupCode,
)


class SignupCodeAdmin(admin.ModelAdmin):
Expand All @@ -29,7 +36,19 @@ class EmailAddressAdmin(AccountAdmin):
search_fields = ["email", "user__username"]


class PasswordExpiryAdmin(admin.ModelAdmin):

raw_id_fields = ["user"]


class PasswordHistoryAdmin(admin.ModelAdmin):

raw_id_fields = ["user"]


admin.site.register(Account, AccountAdmin)
admin.site.register(SignupCode, SignupCodeAdmin)
admin.site.register(AccountDeletion, AccountDeletionAdmin)
admin.site.register(EmailAddress, EmailAddressAdmin)
admin.site.register(PasswordExpiry, PasswordExpiryAdmin)
admin.site.register(PasswordHistory, PasswordHistoryAdmin)
18 changes: 13 additions & 5 deletions account/management/commands/user_password_expiry.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.contrib.auth import get_user_model
from django.core.management.base import LabelCommand

from account.conf import settings
Expand All @@ -6,17 +7,24 @@

class Command(LabelCommand):

help = "Create user-specific password expiration."
help = "Create user-specific password expiration period."
label = "username"

def add_arguments(self, parser):
super(Command, self).add_arguments(parser)
parser.add_argument("-e", "--expire", default=settings.ACCOUNT_PASSWORD_EXPIRY)
parser.add_argument(
"-e", "--expire",
type=int,
nargs="?",
default=settings.ACCOUNT_PASSWORD_EXPIRY,
help="number of seconds until password expires"
)

def handle_label(self, username, **options):
User = get_user_model()
try:
user = settings.AUTH_USER_MODEL.objects.get(username=username)
except settings.AUTH_USER_MODEL.DoesNotExist:
user = User.objects.get(username=username)
except User.DoesNotExist:
return "User \"{}\" not found".format(username)

expire = options["expire"]
Expand All @@ -28,4 +36,4 @@ def handle_label(self, username, **options):
user.password_expiry.expiry = expire
user.password_expiry.save()

return "User \"{}\" password expiration now {} seconds".format(username, expire)
return "User \"{}\" password expiration set to {} seconds".format(username, expire)
45 changes: 45 additions & 0 deletions account/management/commands/user_password_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import datetime
import pytz

from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand

from account.models import PasswordHistory


class Command(BaseCommand):

help = "Create password history for all users without existing history."

def add_arguments(self, parser):
parser.add_argument(
"-d", "--days",
type=int,
nargs="?",
default=10,
help="age of current password (in days)"
)
parser.add_argument(
"-f", "--force",
action="store_true",
help="create new password history for all users, regardless of existing history"
)

def handle(self, *args, **options):
User = get_user_model()
users = User.objects.all()
if not options["force"]:
users = users.filter(password_history=None)

if not users:
return "No users found without password history"

days = options["days"]
timestamp = datetime.datetime.now(tz=pytz.UTC) - datetime.timedelta(days=days)

# Create new PasswordHistory on `timestamp`
PasswordHistory.objects.bulk_create(
[PasswordHistory(user=user, timestamp=timestamp) for user in users]
)

return "Password history set to {} for {} users".format(timestamp, len(users))
5 changes: 3 additions & 2 deletions account/migrations/0003_passwordexpiry_passwordhistory.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-09-01 17:50
# Generated by Django 1.10.1 on 2016-09-13 08:55
from __future__ import unicode_literals

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


class Migration(migrations.Migration):
Expand All @@ -28,7 +29,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=255)),
('timestamp', models.DateTimeField(auto_now=True)),
('timestamp', models.DateTimeField(default=django.utils.timezone.now)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='password_history', to=settings.AUTH_USER_MODEL)),
],
),
Expand Down
20 changes: 0 additions & 20 deletions account/migrations/0004_auto_20160909_1235.py

This file was deleted.

4 changes: 2 additions & 2 deletions account/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def user_post_save(sender, **kwargs):
"""

# Disable post_save during manage.py loaddata
if kwargs.get('raw', False):
if kwargs.get("raw", False):
return False

user, created = kwargs["instance"], kwargs["created"]
Expand Down Expand Up @@ -398,7 +398,7 @@ class PasswordHistory(models.Model):
"""
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="password_history")
password = models.CharField(max_length=255) # encrypted password
timestamp = models.DateTimeField(auto_now_add=True)
timestamp = models.DateTimeField(default=timezone.now) # password creation time


class PasswordExpiry(models.Model):
Expand Down
Loading

0 comments on commit 23edd12

Please sign in to comment.