Skip to content

Commit

Permalink
Merge pull request #136 from Theodrem/command_management_reset_password
Browse files Browse the repository at this point in the history
add  reset_password and update create_superuser_with_email command managements
  • Loading branch information
lcognat committed Oct 19, 2022
2 parents 52a1e78 + f49b487 commit a6612b4
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 47 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@

### Added

- Command management `reset_password`
- Management command to get database connection string, could be used with pg_dump (`pg_dump --dbname=$CONNECTION_STRING`) see [https://www.postgresql.org/docs/14/libpq-connect.html](documentation)

```bash
/usr/bin/pg_dump --dbname=`python3 manage.py db_connection_string` | bzip2 --fast > backup.sql.bzip2
```
- nothing added

### Changed

- Command management `check_superuser_with_email`: Now we check if any users exists not only superuser
- The datetimefomat accept two formats `yyyy-mm-ddTHH-mm-ssZ` and `yyyy-mm-ddTHH-mm-ss.SSSSSSZ`
- Disable trimmimng the leading and trailing whitespaces within CharField and TextField serializers.


### Removed

- nothing removed
Expand Down
24 changes: 12 additions & 12 deletions concrete_datastore/concrete/management/commands/create_superuser.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ class Command(BaseCommand):

def add_arguments(self, parser):
parser.add_argument('email', type=str, help='choosen email')
parser.add_argument('--password', type=str, help='raw password')
parser.add_argument('password', type=str, help='choosen password')

def handle(self, *args, **options):
UserClass = get_user_model()
kwargs = {
'email': options['email'].lower(),
'admin': True,
'is_superuser': True,
'is_staff': True,
}
instance, created = UserClass.objects.get_or_create(**kwargs)
if options['password'] is not None:
instance.set_password(options['password'])
instance.save()
UserModel = get_user_model()
email = options['email'].lower()

user, created = UserModel.objects.get_or_create(email=email)

if created:
user.set_level('superuser')
if options['password'] is not None:
user.set_password(options['password'])

user.save()
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
# coding: utf-8
import random
import string

from django.apps import apps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from concrete_datastore.concrete.management.commands.reset_password import (
generate_random_password,
get_admin_url,
)


class Command(BaseCommand):
Expand All @@ -18,50 +20,22 @@ def handle(self, *args, **options):
concrete = apps.get_app_config('concrete')
EmailModel = concrete.models['email']
UserModel = get_user_model()
if options['email'] is None:
raise ValueError("'email' is needed to create a user")

email = options['email'].lower()

kwargs = {
'email': email,
'admin': True,
'is_superuser': True,
'is_staff': True,
}

user, created = UserModel.objects.get_or_create(**kwargs)
user, created = UserModel.objects.get_or_create(email=email)

if created:
password = ''.join(
random.choice(string.ascii_letters + string.digits) # nosec
for _ in range(24)
)
password = generate_random_password()
user.set_password(password)
user.set_level('superuser')
user.save()

port = ''
if int(settings.PORT) not in (80, 443):
port = ':{}'.format(settings.PORT)

admin_url = '{}://{}{}/concrete-datastore-admin/'.format(
settings.SCHEME, settings.HOSTNAME, port
)
admin_url = get_admin_url()
email_instance = EmailModel(
created_by=user,
subject='Access to Concrete Instance',
body='''
Welcome to Concrete <a href="{admin_url}">{hostname}</a><br>
<br>
You can now connect to your concrete instance with the following
credentials :<br>
email {email}<br>
password {password}<br>
<br>
Please change your password as you connect for the first time.
'''.format(
body=settings.CREATE_SUPERUSER_EMAIL_MESSAGE_BODY.format(
hostname=settings.HOSTNAME,
admin_url=admin_url,
email=email,
Expand Down
63 changes: 63 additions & 0 deletions concrete_datastore/concrete/management/commands/reset_password.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# coding: utf-8
import random
import string

from django.apps import apps
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from django.core.management.base import CommandError


def generate_random_password(
length=24, chars=string.ascii_letters + string.digits
):
return ''.join(random.choice(chars) for _ in range(length))


def get_admin_url():
port = ''
if int(settings.PORT) not in (80, 443):
port = ':{}'.format(settings.PORT)

admin_url = '{}://{}{}/concrete-datastore-admin/'.format(
settings.SCHEME, settings.HOSTNAME, port
)
return admin_url


class Command(BaseCommand):
help = 'Reset password'

def add_arguments(self, parser):
parser.add_argument('email', type=str, help='user email')

def handle(self, *args, **options):
concrete = apps.get_app_config('concrete')
EmailModel = concrete.models['email']
UserModel = get_user_model()
email = options['email'].lower()

try:
user = UserModel.objects.get(email=email)
except UserModel.DoesNotExist:
raise CommandError(f"This email: {email} does not exist")

password = generate_random_password()
user.set_password(password)
user.save()

admin_url = get_admin_url()
email_instance = EmailModel(
created_by=user,
subject='Reset password to Concrete Instance',
body=settings.RESET_PASSWORD_EMAIL_MESSAGE_BODY.format(
hostname=settings.HOSTNAME,
admin_url=admin_url,
email=email,
password=password,
),
receiver=user,
)

email_instance.save()
30 changes: 30 additions & 0 deletions concrete_datastore/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,36 @@
"""
)

CREATE_SUPERUSER_EMAIL_MESSAGE_BODY = ( # nosec
'''
Welcome to Concrete <a href="{admin_url}">{hostname}</a><br>
<br>
You have created a new superuser.
You can now connect to your concrete instance with the following
credentials :<br>
email {email}<br>
password {password}<br>
<br>
Please change your password as you connect for the first time.
'''
)

RESET_PASSWORD_EMAIL_MESSAGE_BODY = ( # nosec
'''
Welcome to Concrete <a href="{admin_url}">{hostname}</a><br>
<br>
You have requested a new password.
You can now connect to your concrete instance with the following
credentials :<br>
email {email}<br>
password {password}<br>
<br>
Please change your password as you connect for the first time.
'''
)

# fmt:on


Expand Down
41 changes: 41 additions & 0 deletions tests/tests_command_manager/test_create_superuser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# coding: utf-8
from django.core.management import call_command
from django.core.management.base import CommandError


from django.test import TestCase

from concrete_datastore.concrete.models import User


class CreateSuperUserTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='johndoe@netsach.org', password='plop'
)
self.user.save()

def test_create_superuser(self):
self.assertEqual(User.objects.all().count(), 1)
call_command("create_superuser", "new_superuser@netsach.org", "plop")
new_superuser = User.objects.get(email="new_superuser@netsach.org")

self.assertEqual(User.objects.all().count(), 2)

self.assertEqual(new_superuser.level, "superuser")
self.assertTrue(new_superuser.admin)
self.assertTrue(new_superuser.check_password("plop"))

def test_create_superuser_email_already_exist(self):
self.assertEqual(User.objects.all().count(), 1)
call_command("create_superuser", "johndoe@netsach.org", "plop")

self.assertEqual(User.objects.all().count(), 1)

def test_create_superuser_args_are_none(self):
with self.assertRaises(CommandError) as e:
call_command('create_superuser')
self.assertEqual(
str(e.exception),
"Error: the following arguments are required: email, password",
)
48 changes: 48 additions & 0 deletions tests/tests_command_manager/test_create_superuser_with_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from django.core.management import call_command
from django.core.management.base import CommandError


from django.test import TestCase

from concrete_datastore.concrete.models import User, Email


class CreateSuperUserWithEmailTests(TestCase):
def setUp(self):
self.super_user = User.objects.create_user(
email='super_user@netsach.org', password='plop'
)
self.super_user.save()

def test_create_superuser(self):
self.assertEqual(User.objects.all().count(), 1)
call_command(
'create_superuser_with_email', "new_superuser@netsach.org"
)
new_superuser = User.objects.get(email="new_superuser@netsach.org")
email_sent = Email.objects.get(created_by=new_superuser)

self.assertEqual(Email.objects.all().count(), 1)
self.assertEqual(User.objects.all().count(), 2)

self.assertIn("Welcome to Concrete", email_sent.body)
self.assertEqual(new_superuser.level, "superuser")
self.assertTrue(new_superuser.admin)
self.assertEqual("Access to Concrete Instance", email_sent.subject)

def test_create_superuser_email_exists(self):
self.assertEqual(User.objects.all().count(), 1)

call_command('create_superuser_with_email', self.super_user.email)
self.assertEqual(Email.objects.all().count(), 0)
self.assertEqual(User.objects.all().count(), 1)

def test_create_superuser_email_is_none(self):
with self.assertRaises(CommandError) as e:
call_command('create_superuser_with_email')
self.assertEqual(
str(e.exception),
"Error: the following arguments are required: email",
)

self.assertEqual(0, Email.objects.all().count())
45 changes: 45 additions & 0 deletions tests/tests_command_manager/test_reset_password.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from django.core.management import call_command
from django.core.management.base import CommandError
from django.test import TestCase

from concrete_datastore.concrete.models import User, Email


class ResetPasswordCommandManagementTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(
email='johndoe@netsach.org', password='plop'
)
self.user.save()

def test_reset_password_user(self):
self.assertTrue(self.user.check_password("plop"))
call_command('reset_password', self.user.email)
email_sent = Email.objects.get(created_by=self.user)
self.user.refresh_from_db()

self.assertEqual(1, Email.objects.all().count())
self.assertIn("You have requested a new password", email_sent.body)
self.assertEqual(
"Reset password to Concrete Instance", email_sent.subject
)
self.assertFalse(self.user.check_password("plop"))

def test_reset_password_email_does_not_exist(self):
email = "anonymous@netsach.org"
self.assertEqual(Email.objects.all().count(), 0)
with self.assertRaises(CommandError) as e:
call_command('reset_password', email)
self.assertEqual(
str(e.exception), f"This email: {email} does not exist"
)

def test_reset_password_email_is_none(self):
with self.assertRaises(CommandError) as e:
call_command('reset_password')
self.assertEqual(
str(e.exception),
"Error: the following arguments are required: email",
)

self.assertEqual(Email.objects.all().count(), 0)

0 comments on commit a6612b4

Please sign in to comment.