How to access custom admin registration page with access code

Hi Devs community, sorry I’m new to this forum and am also a beginner in Django. I’ve been working on a project that has three custom users. These users are admin, customer, and employee. Each has their own registration page that one could access and sign up easily. However, I want to make sure that for the admin registration page, it has to be accessed using an admin code so that only the website owners can register as admin and manage the website. I did that and it worked just fine. I wanted to add a custom decorator to enforce this but it’s not working as I anticipated. Here’s the link to my project on GitHub


GitHub - billwhite16/custom_user: This project has three users . I would really appreciate any help or guidance on this. Thank you in advance.

Please avoid sharing code via screenshots. You can post code then wrap it with three backticks (```) before and after. The reason is that we can’t copy/paste from it so we would have to retype it out. Plus it’s difficult for search engines to parse and index it (if at all).

This is good to know, but we need more information. In this case we need to know what you tried (probably the view that you applied it to, the decorator it self(already provided), and then what did you do with the application itself to hit that view), what happened (did you get an error, did something not submit, etc) and what did you expect to happen.

Your test function in user_passes_test tests for the admin_code_entered attribute on the user object, but it seems you dont save the code on the user object anyhere, thus the test would always return false and render the code input form (if I understood your view flow correctly).

There are 2 ways to fix it:

  1. Make the admin code a user attribute, which you can store on the user during post processing of AdminCodeForm. Then it can be populated during next request and correctly evaled from user_passes_test. This would not be my first choice, as it makes temporary form data (the admin code) persitant between AdminCodeForm and AdminSignUpForm request/response cycling. This also only works correctly, if the user existed before and is already logged in, but gets “promoted” to admin realms. This is def. pretty involved to get done right (or some random user might get the admin code by accident).
  2. Move the admin code test from user_passes_test into the view itself. With this you can just grab the entered value from POST params and eval directly. If you also want to secure the later admin signup form as well (yes you should, otherwise ppl could just skip the admin code form), you can pass it on as hidden value and re-eval during AdminSignUpForm post processing.

Edit:
There is a third and prolly the easiest way to fix it - how about asking for the admin code along with signup credentials in just one form? That would save you the headache carrying the admin code forward between several forms and request/response cycles.

1 Like

Hi @jerch thank you for you prompt response. I think your suggestion of trying the third option to solving this issue is a really good idea. I would appreciate if you give an example code on how this should be implemented. Here’s the link to the project on GitHub if you’d like to customize it GitHub - billwhite16/custom_user: This project has three users. Thanks in advance

Hi @CodenameTim thank you for taking the time to attend to my issue and my apologies for attaching screenshots. Well what I’m actually trying to do is to make sure that no one access the custom admin registration page without entering the admin code. And so I have come up with a decorator to handle this but it’s not taking me to the admin register page as I anticipated, in fact when I entered the correct code as well, it’s still redirecting to the same admin validate page. I have identified this issue which is in my template file and have fixed it () in my admin_code.html template. However when I entered the correct code and successfully go to the admin registration page, the admin register form appears to be not working perfectly as it was before (without the decorator). All I could see on each fields on the admin register form is this error “This field is required” and when I clicked submit without filling any field, it just take me to back to the admin validate page without validating the fields. It should validate the fields and if all the information is valid, it will create an admin account and redirect to the login page. But it’s not doing that.

Here is the code in decorators.py:
“”"
from django.contrib.auth.decorators import user_passes_test
from django.urls import reverse_lazy
from django.conf import settings

#version 1
def admin_code_entered(function=None):
actual_decorator = user_passes_test(
lambda u: hasattr(u, ‘admin_code_entered’) and u.admin_code_entered == settings.ADMIN_CODE,
login_url=reverse_lazy(‘admin_validate’),
redirect_field_name=None
)
if function:
return actual_decorator(function)
return actual_decorator
“”"

Here is the code in views.py:
“”"
from django.contrib.auth import login, logout, authenticate
from django.shortcuts import redirect, render
from django.contrib import messages
from . forms import AdminCodeForm, AdminSignUpForm, CustomerSignUpForm, EmployeeSignUpForm
from django.contrib.auth.forms import AuthenticationForm
from django.conf import settings
from .decorators import admin_code_entered

#register general view
def register(request):
return render(request, ‘register.html’)

#version1-the admin registration validation view
def admin_validate(request):
form = AdminCodeForm(request.POST or None)
if form.is_valid():
if form.cleaned_data[‘admin_code’] == settings.ADMIN_CODE:
return redirect(‘admin_register’)
else:
form.add_error(‘admin_code’, ‘Incorrect admin code.’)
return render(request, ‘admin_code.html’, {‘form’: form})

#@admin_code_entered
def admin_register(request):
if request.method == ‘POST’:
form = AdminSignUpForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect(‘login’)
else:
form = AdminSignUpForm()
return render(request, ‘admin_register.html’, {‘form’: form})
“”"

Here’s the code in my forms.py:
“”"
from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.db import transaction
from .models import User, Admin, Customer, Employee
from django.conf import settings

#the user will have to enter the admin code inorder to register as admin
class AdminCodeForm(forms.Form):
admin_code = forms.CharField(max_length=50)
def clean_admin_code(self):
admin_code = self.cleaned_data.get(‘admin_code’)
if admin_code != settings.ADMIN_CODE:
raise forms.ValidationError(‘Invalid admin code’)
return admin_code

#admin registration form
class AdminSignUpForm(UserCreationForm):
email = forms.EmailField(required=True)
first_name = forms.CharField(required=True)
last_name = forms.CharField(required=True)
phone_number = forms.CharField(required=True)
position = forms.CharField(required=True)

class Meta(UserCreationForm.Meta):
    model = User

#verify email
def clean_email(self):
    email = self.cleaned_data.get('email')
    if User.objects.filter(email=email).exists():
        raise forms.ValidationError('This email address is already in use.')
    return email

@transaction.atomic
def save(self):
    user = super().save(commit=False)
    user.email = self.cleaned_data.get('email')
    user.is_superuser = True
    user.is_staff = True
    user.is_admin = True
    user.first_name = self.cleaned_data.get('first_name')
    user.last_name = self.cleaned_data.get('last_name')
    user.save()
    admin = Admin.objects.create(user=user)
    admin.phone_number=self.cleaned_data.get('phone_number')
    admin.position=self.cleaned_data.get('position')
    admin.save()
    return user

“”"

Please note that the admin code is hard coded in the project settings.

Here is the link to the project on GitHub if you’d like to do further investigation or customize GitHub - billwhite16/custom_user: This project has three users.

Thank you in advance for you help.

Please edit your post to format your code.

Consider this code when request.POST is None (a GET request). The form won’t have any entries in the data, but it will have called form.is_valid() which expects there to be valid data. This is why you’re getting a bunch of “This field is required” messages.

In this case, what happens to form when form.is_valid() returns False? This is why you’re not getting any validation errors.