From f81d07e631f83721c5aa5ee5324941896cd2b6be Mon Sep 17 00:00:00 2001 From: HimanshuSharma-Angel Date: Mon, 8 Jul 2024 19:04:21 +0530 Subject: [PATCH 1/2] Initial commit for django admin. --- .github/workflows/staging.yml | 1 + apps/accounts/management/commands/__init__.py | 0 .../management/commands/create_admin.py | 18 ++++++ apps/accounts/models.py | 60 ++++++++++++++++++- apps/accounts/serializers.py | 24 ++++++++ apps/accounts/urls.py | 3 +- apps/accounts/views.py | 34 ++++++++++- apps/applicants/admin.py | 2 + apps/jobs/admin.py | 6 +- apps/jobs/models.py | 2 +- apps/userprofile/admin.py | 5 +- docker-compose.yml | 2 +- null_jobs_backend/settings.py | 5 ++ requirements.txt | 1 - 14 files changed, 154 insertions(+), 9 deletions(-) create mode 100644 apps/accounts/management/commands/__init__.py create mode 100644 apps/accounts/management/commands/create_admin.py diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index acc466a..f8a337e 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -44,5 +44,6 @@ jobs: docker stop null_jobs_backend || true docker rm null_jobs_backend || true docker run -d --network null_jobs --env-file /home/dev@null/projects/nulljobs/backend/.env null_jobs_backend:latest python manage.py migrate + docker run -d --network null_jobs --env-file /home/dev@null/projects/nulljobs/backend/.env null_jobs_backend:latest python manage.py create_superuser docker run -d --name null_jobs_backend --network null_jobs --restart always --env-file /home/dev@null/projects/nulljobs/backend/.env null_jobs_backend:latest python manage.py runserver 0.0.0.0:8000 rm -r /tmp/null_job_backend_latest.tar diff --git a/apps/accounts/management/commands/__init__.py b/apps/accounts/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/accounts/management/commands/create_admin.py b/apps/accounts/management/commands/create_admin.py new file mode 100644 index 0000000..83f7f64 --- /dev/null +++ b/apps/accounts/management/commands/create_admin.py @@ -0,0 +1,18 @@ +from django.core.management.base import BaseCommand +from django.contrib.auth import get_user_model + + +class Command(BaseCommand): + help = 'Create an initial admin User' + + def handle(self, *args, **kwargs): + User = get_user_model() + if not User.objects.filter(email='', user_type="Moderator").exists(): + User.objects.create_superuser( + email='', + name='', + password='' + ) + self.stdout.write(self.style.SUCCESS('Successfully created the admin user')) + else: + self.stdout.write(self.style.WARNING('Admin user already exists')) diff --git a/apps/accounts/models.py b/apps/accounts/models.py index ea54f77..e60daf6 100644 --- a/apps/accounts/models.py +++ b/apps/accounts/models.py @@ -6,7 +6,7 @@ # from Jobapp.models import User as JobUser # Create your models here. -USER_TYPE = (("Job Seeker", "User/Employee"), ("Employer", "HR/Employer")) +USER_TYPE = (("Job Seeker", "User/Employee"), ("Employer", "HR/Employer"), ("Moderator", "Admin/Moderator")) class UserManager(BaseUserManager): @@ -33,12 +33,35 @@ def create_superuser(self, email, name, password=None): email, password=password, name=name, + user_type="Moderator" # keep it as it is ) user.is_admin = True + user.is_staff = True + user.is_moderator = True + user.is_verified = True + user.save(using=self._db) + return user + + # todo: to have it when we have moderator dashboard + def create_moderator(self, email, name, password=None): + """ + Creates and saves a superuser with the given email, name, tc and password. + """ + user = self.create_user( + email, + password=password, + name=name, + user_type="Moderator" # as it moderator + ) + # + user.is_admin = False # false + user.is_staff = True + user.is_moderator = True user.save(using=self._db) return user + class User(AbstractBaseUser): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) @@ -51,6 +74,8 @@ class User(AbstractBaseUser): is_admin = models.BooleanField(default=False, editable=False) is_moderator = models.BooleanField(default=False, null=True, editable=False) + is_staff = models.BooleanField(default=False) + created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -72,3 +97,36 @@ class User(AbstractBaseUser): class Meta: db_table = "tbl_user_auth" + + + def get_full_name(self): + # The user is identified by their email address + return self.email + + def get_short_name(self): + # The user is identified by their email address + return self.email + + def __str__(self): + return self.email + + def has_perm(self, perm, obj=None): + "Does the user have a specific permission?" + # Simplest possible answer: Yes, always + return True + + def has_module_perms(self, app_label): + "Does the user have permissions to view the app `app_label`?" + # Simplest possible answer: Yes, always + return True + + @property + def is_staff_member(self): + "Is the user a member of staff?" + return self.is_staff + + @property + def is_admin_member(self): + "Is the user a admin member?" + return self.is_admin + diff --git a/apps/accounts/serializers.py b/apps/accounts/serializers.py index f706232..d1afe0d 100644 --- a/apps/accounts/serializers.py +++ b/apps/accounts/serializers.py @@ -265,3 +265,27 @@ def validate(self, attrs): def create(self, validate_data): return User.objects.create_user(**validate_data) + +from django.contrib.auth import get_user_model + +User = get_user_model() + +class AdminUserSerializer(serializers.ModelSerializer): + password = serializers.CharField(write_only=True) + + class Meta: + model = User + fields = ['email', 'name', 'password'] + + def create(self, validated_data): + user = User( + email=validated_data['email'], + name=validated_data['name'], + ) + user.set_password(validated_data['password']) + user.is_staff = True + user.is_superuser = True # for time being lets keep it this only + user.user_type = "Moderator" + user.is_verified = True + user.save() + return user diff --git a/apps/accounts/urls.py b/apps/accounts/urls.py index 7c54abc..8db9135 100644 --- a/apps/accounts/urls.py +++ b/apps/accounts/urls.py @@ -12,7 +12,7 @@ UserLogOutView, UserPasswordResetView, UserProfileView, - UserRegistrationView, + UserRegistrationView, CreateAdminUserView, ) app_name = "apps.accounts" @@ -52,4 +52,5 @@ # google oauth endpoints path("google/login/", GoogleHandle.as_view(), name="google"), path("google/login/callback/", CallbackHandleView.as_view(), name="callback"), + path('create-admin', CreateAdminUserView.as_view(), name='create-admin'), ] diff --git a/apps/accounts/views.py b/apps/accounts/views.py index 8dec6d0..4d13a2b 100644 --- a/apps/accounts/views.py +++ b/apps/accounts/views.py @@ -9,7 +9,7 @@ from django.conf import settings from django.contrib.auth import authenticate from drf_spectacular.utils import OpenApiParameter, extend_schema -from rest_framework import status +from rest_framework import status, generics from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView @@ -28,10 +28,11 @@ UserPasswordResetSerializer, UserRegistrationResponseSerializer, UserRegistrationSerializer, - UserSerializer, + UserSerializer, AdminUserSerializer, ) from apps.accounts.utils import * from apps.userprofile.models import UserProfile +import logging # from django.shortcuts import render @@ -473,3 +474,32 @@ def get(self, request): {"msg": "There was an error authenticating the user"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) + + + + +class IsSuperuserOrStaff(IsAuthenticated): + def has_permission(self, request, view): + return super().has_permission(request, view) and (request.user.is_admin or request.user.is_staff) + + +class CreateAdminUserView(generics.CreateAPIView): + serializer_class = AdminUserSerializer + permission_classes = [IsSuperuserOrStaff] + + def create(self, request, *args, **kwargs): + # Check if the current user email matches the one in the environment variable + admin_email_env = os.getenv('DJANGO_ADMIN_EMAIL') + admin_password_env = os.getenv('DJANGO_ADMIN_PASSWORD') + if not (admin_email_env or admin_password_env): + return Response({'error': 'Admin email is not set in environment variables'}, + status=status.HTTP_400_BAD_REQUEST) + + if request.user.email != admin_email_env and request.user.password != admin_password_env: + return Response({'error': 'You are not authorized to create a new superuser'}, + status=status.HTTP_403_FORBIDDEN) + + response = super().create(request, *args, **kwargs) + if response.status_code == status.HTTP_201_CREATED: + logger.info(f"Admin user created: {response.data['email']} by {request.user.email}") + return response diff --git a/apps/applicants/admin.py b/apps/applicants/admin.py index 8c38f3f..f886e12 100644 --- a/apps/applicants/admin.py +++ b/apps/applicants/admin.py @@ -1,3 +1,5 @@ from django.contrib import admin +from .models import Applicants +admin.site.register(Applicants) # Register your models here. diff --git a/apps/jobs/admin.py b/apps/jobs/admin.py index 8c38f3f..7125af0 100644 --- a/apps/jobs/admin.py +++ b/apps/jobs/admin.py @@ -1,3 +1,7 @@ from django.contrib import admin - +from .models import Job, ContactMessage, Company # Register your models here. + +admin.site.register(Job) +admin.site.register(ContactMessage) +admin.site.register(Company) \ No newline at end of file diff --git a/apps/jobs/models.py b/apps/jobs/models.py index 9b435f9..5543678 100644 --- a/apps/jobs/models.py +++ b/apps/jobs/models.py @@ -89,7 +89,7 @@ class Meta: updated_at = models.DateTimeField(auto_now=True) # update timestamp on every save() # flags are un explained here - is_active = models.BooleanField(default=False, null=False, editable=False) + is_active = models.BooleanField(default=False, null=False) is_created = models.BooleanField(default=False, null=True, editable=False) is_deleted = models.BooleanField(default=False, null=True, editable=False) is_featured = models.BooleanField(default=False, null=True) diff --git a/apps/userprofile/admin.py b/apps/userprofile/admin.py index 8c38f3f..55f139b 100644 --- a/apps/userprofile/admin.py +++ b/apps/userprofile/admin.py @@ -1,3 +1,6 @@ from django.contrib import admin - +from .models import UserProfile, FavoriteProfiles # Register your models here. + +admin.site.register(UserProfile) +admin.site.register(FavoriteProfiles) diff --git a/docker-compose.yml b/docker-compose.yml index 97cb36d..a06bd1e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,7 +28,7 @@ services: condition: service_healthy volumes: - .:/workspace - command: bash -c "python manage.py loaddata utils/seed/accounts/userauth.json && python manage.py loaddata utils/seed/jobs/company.json && python manage.py loaddata utils/seed/jobs/userprofile.json && python manage.py loaddata utils/seed/jobs/job.json" +# command: bash -c "python manage.py loaddata utils/seed/accounts/userauth.json && python manage.py loaddata utils/seed/jobs/company.json && python manage.py loaddata utils/seed/jobs/job.json && python manage.py loaddata utils/seed/jobs/drf-spectacular.json" env_file: - .env diff --git a/null_jobs_backend/settings.py b/null_jobs_backend/settings.py index 7f062ec..90714c6 100644 --- a/null_jobs_backend/settings.py +++ b/null_jobs_backend/settings.py @@ -107,6 +107,11 @@ "PASSWORD": os.getenv("DB_PWD"), "HOST": os.getenv("DB_HOST"), "PORT": os.getenv("DB_PORT"), + }, + 'local': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'mydatabase', # This is where you put the name of the db file. + # If one doesn't exist, it will be created at migration time. } } diff --git a/requirements.txt b/requirements.txt index 57161d8..3a0eaf8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,7 +33,6 @@ jsonschema-specifications==2023.12.1 lazy-object-proxy==1.9.0 MarkupSafe==2.1.3 mccabe==0.7.0 -mysqlclient==2.2.0 nodeenv==1.8.0 packaging==23.1 pkgutil_resolve_name==1.3.10 From cf4a2413f07affd0c94382706a95593dee9cc657 Mon Sep 17 00:00:00 2001 From: Himanshu Sharma <95011614+HimanshuSharma-Angel@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:49:08 +0530 Subject: [PATCH 2/2] Update requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 3a0eaf8..57161d8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,6 +33,7 @@ jsonschema-specifications==2023.12.1 lazy-object-proxy==1.9.0 MarkupSafe==2.1.3 mccabe==0.7.0 +mysqlclient==2.2.0 nodeenv==1.8.0 packaging==23.1 pkgutil_resolve_name==1.3.10