Browse Source
feat: implement user authentication, password management, and course participant views with encryption options
master
feat: implement user authentication, password management, and course participant views with encryption options
master
9 changed files with 162 additions and 10 deletions
-
55apps/account/management/commands/set_default_passwords.py
-
18apps/account/migrations/0005_user_password_enc.py
-
42apps/account/models/user.py
-
11apps/account/serializers/user.py
-
8apps/account/views/user.py
-
7apps/course/views/participant.py
-
17apps/quiz/migrations/0004_alter_question_options.py
-
11nginx/imamjavad.conf
-
1requirements.txt
@ -0,0 +1,55 @@ |
|||
import secrets |
|||
from django.core.management.base import BaseCommand |
|||
from django.db.models import Q |
|||
from apps.account.models import User |
|||
|
|||
|
|||
class Command(BaseCommand): |
|||
help = 'Sets a new plain/hashed password and encrypts it for active users who lack an encrypted password.' |
|||
|
|||
def add_arguments(self, parser): |
|||
parser.add_argument( |
|||
'--password', |
|||
type=str, |
|||
help='Specific password to set for all matched users. If not provided, a random 8-character password will be generated for each user.' |
|||
) |
|||
parser.add_argument( |
|||
'--dry-run', |
|||
action='store_true', |
|||
help='Run the command without saving changes to the database.' |
|||
) |
|||
|
|||
def handle(self, *args, **options): |
|||
password_input = options['password'] |
|||
dry_run = options['dry_run'] |
|||
|
|||
# Find active users without an encrypted password |
|||
users = User.objects.filter( |
|||
Q(password_enc='') | Q(password_enc__isnull=True), |
|||
is_active=True |
|||
) |
|||
|
|||
if not users.exists(): |
|||
self.stdout.write(self.style.SUCCESS("No active users found without an encrypted password.")) |
|||
return |
|||
|
|||
self.stdout.write(f"Found {users.count()} active users without an encrypted password.") |
|||
|
|||
updated_count = 0 |
|||
for user in users: |
|||
# Generate or use the provided password |
|||
new_password = password_input if password_input else secrets.token_urlsafe(6)[:8] |
|||
|
|||
self.stdout.write(f"User: {user.email or user.fullname} (ID: {user.id}) -> Password: {new_password}") |
|||
|
|||
if not dry_run: |
|||
user.set_password(new_password) |
|||
user.set_plain_password(new_password) |
|||
user.save() |
|||
|
|||
updated_count += 1 |
|||
|
|||
if dry_run: |
|||
self.stdout.write(self.style.WARNING(f"[DRY-RUN] Would have updated {updated_count} users.")) |
|||
else: |
|||
self.stdout.write(self.style.SUCCESS(f"Successfully updated {updated_count} users.")) |
|||
@ -0,0 +1,18 @@ |
|||
# Generated by Django 5.2.12 on 2026-06-01 15:39 |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('account', '0004_consultantuser_alter_user_user_type'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='user', |
|||
name='password_enc', |
|||
field=models.CharField(blank=True, default='', help_text='Encrypted copy of password for displaying in the admin panel.', max_length=512, verbose_name='Encrypted Password'), |
|||
), |
|||
] |
|||
@ -0,0 +1,17 @@ |
|||
# Generated by Django 5.2.12 on 2026-06-01 15:39 |
|||
|
|||
from django.db import migrations |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('quiz', '0003_quiz_course'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterModelOptions( |
|||
name='question', |
|||
options={'ordering': ('priority', 'id'), 'verbose_name': 'Question', 'verbose_name_plural': 'Questions'}, |
|||
), |
|||
] |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue