'How do I make signup page avilable only for logged in staff users in Django allauth?
I'm very new to Django.
I used allauth to make email verification and user management system simple.
I want a system where only admins (staff users) can signup users.
But as it is now signup page is only available for not logged in users.
How do I make signup page available only for logged in staff users in Django allauth?
What I tried:
I tried to add custom view, url and template to customize access to signup page and form.
I added this on urls.py:
path('accounts/signup/',views.user_register_view, name='signup'),
And this on views.py:
@login_required
def user_register_view(request):
if request.user.is_staff:
return render(request, "account/signup.html")
else:
reverse_lazy('users:dashboard')
And added a template file in template/account/signup.html, just copied the text from here: https://github.com/pennersr/django-allauth/blob/master/allauth/templates/account/signup.html
And added a custom text just to see if my custom template is viewed.
What happened is just that when I sign in as admin, it instantly redirects to the signup page despite I set LOGIN_REDIRECT_URL = 'users:dashboard' in settings.py
And while only admins can access the accounts/signup page, alle the fields disappeared. This is how it looks like when you view the source:
<br>THIS IS A CUSTOM TEMPLATE</br>
<h1>Sign Up</h1>
<p>Already have an account? Then please <a href="">sign in</a>.</p>
<form class="signup" id="signup_form" method="post" action="/accounts/signup/">
<input type="hidden" name="csrfmiddlewaretoken" value="muuodB6QqTD1BBxfIj7VW16qvjx1S7OUwoUf0xBNy6WuaLSE03228uMRxjJ2COjJ">
<button type="submit">Sign Up »</button>
</form>
How do I make signup page available only for logged in staff users in Django allauth?
Solution 1:[1]
I found some solution or "workaround" to this. I don't know how good or bad it is, but at least it works as expected.
First: Set this to False on settings.py so that users aren't automatically redirected depending on they're logged in or not. This will make the signup and login form available to all users whether they're logged in or not.
ACCOUNT_AUTHENTICATED_LOGIN_REDIRECTS = False
Second: Alter the form_valid function on a custom SignUpview a little so that even if users post registration in the page, it won't be allowed.
You just add these two lines so that the function raises error if the user is not staff.
if not self.request.user.is_staff: # ADDED LINE 1: Check if User is staff
raise Exception("Error: user is not staff") # ADDED LINE 2: Raise Exception
This is how the CustomSignupView looks:
from allauth.account.views import SignupView
from django.utils.decorators import method_decorator
from django.views.decorators.debug import sensitive_post_parameters
from allauth.exceptions import ImmediateHttpResponse
from allauth.account import app_settings
from allauth.account.utils import (
complete_signup
)
INTERNAL_RESET_SESSION_KEY = "_password_reset_key"
sensitive_post_parameters_m = method_decorator(
sensitive_post_parameters("oldpassword", "password", "password1", "password2")
)
class AccountSignupView(SignupView):
def form_valid(self, form):
# By assigning the User to a property on the view, we allow subclasses
# of SignupView to access the newly created User instance
if not self.request.user.is_staff: # ADDED LINE 1: Check if User is staff
raise Exception("Error: user is not staff") # ADDED LINE 2: Raise Exception
self.user = form.save(self.request)
try:
return complete_signup(
self.request,
self.user,
app_settings.EMAIL_VERIFICATION,
self.get_success_url(),
)
except ImmediateHttpResponse as e:
return e.response
account_signup_view = AccountSignupView.as_view()
And you probably know you have to put this path on urls.py too before the path to include(allauth.urls). I had allauth on project level, so urls are on project urls.py.
path("accounts/signup/", view=views.account_signup_view)
**Third: **Put a simple logic in template like {% if user.is_staff %} to determine who can show the form or not. Other users visiting signup page won't be redirected off automatically, but they'll see another page rendered. Here's an example:
{% extends "base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Signup" %}{% endblock %}
{% block content %}
<br>THIS IS A CUSTOM TEMPLATE</br>
{% if user.is_authenticated and user.is_staff %}
<h1>{% trans "Sign Up" %}</h1>
<p>{% blocktrans %}Already have an account? Then please <a href="{{ login_url }}">sign in</a>.{% endblocktrans %}</p>
<form class="signup" id="signup_form" method="post" action="{% url 'account_signup' %}">
{% csrf_token %}
{{ form.as_p }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button type="submit">{% trans "Sign Up" %} »</button>
</form>
{% else %}
Only admins are allowed to register new users
{% endif %}
{% endblock %}
I don't know how secure or good this solution is, but at least it prevents the all users to use signup form and only allows staff users. If you CTRL + click on a class or function, or visit its GitHub page, you can find its source code, study how it works and copy paste and alter it.
Solution 2:[2]
I hope you mean that you want only staff to create a user account and restrict the sign up for normal users, you can simply do this by creating a user_create view and checking if the authenticated user has the role "staff" to use the view and deleting the sign up from the urls.py also you can do this by the Django admin panel if you have already allowed your staff to use it
Solution 3:[3]
You can create a decorator to check if the user is an admin. For example, in a file called "decorators.py":
from functools import wraps
from django.http import HttpResponseRedirect
def admin_zone(view_func):
def _decorator(request, *args, **kwargs):
if request.user.is_staff:
return view_func(request, *args, **kwargs)
else:
return HttpResponseRedirect('/') #If the user is not an admint, return him where you want...
return wraps(view_func)(_decorator)
To use the decorator in your accounts urls you need a third party plugin called django-decorator-include https://github.com/twidi/django-decorator-include. Install it:
pip install django-decorator-include
Now, you can use your decorator from the urls.py:
from django.contrib import admin
from django.urls import path
from .decorators import admin_zone
from decorator_include import decorator_include
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', decorator_include(admin_zone, 'allauth.urls')),
]
So, you are adding the "admin_zone" decorator to all the urls inside "allauth.urls".
Solution 4:[4]
from django.contrib.auth.mixins import UserPassesTestMixin
# Create your views here.
class AccountSignupView(UserPassesTestMixin, SignupView):
template_name = "users/signup.html"
def test_func(self):
return self.request.user.is_staff
Use the built in django UserPassesTestMixin to achieve this
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Ghassen JemaĆ® |
| Solution 3 | LaCharcaSoftware |
| Solution 4 | Bert Blommers |
