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