UserCreateForm bypassing UserManager, normal users created with UserCreateForm CAN authenticate, but superuser created in CAN shell NOT?

I have a custom model and a custom manager defined like this:

/accounts/models.py

from django.contrib.auth.models import (
    AbstractBaseUser,
    BaseUserManager,
    PermissionsMixin
)
from django.db import models
from django.utils import timezone


class UserManager(BaseUserManager):
    def create_user(self, email, first_name, last_name, username=None, password=None):
        if not email:
            raise ValueError("Users must have a valid email address")

        if not first_name and last_name:
            raise ValueError("Users must have a first and last name")

        created_username = ''.join([first_name.lower(), last_name[:1].lower()])
        i=2
        while User.objects.filter(username=created_username).exists():
            created_username = ''.join([first_name.lower(), last_name[:i].lower()])
            i+=1

        user = self.model(
            email=self.normalize_email(email),
            first_name=first_name,
            last_name=last_name,
            username=created_username
        )

        user.set_password(password)
        user.save()

        return user

    def create_superuser(self, email, first_name, last_name, password):
        user = self.create_user(
            email,
            first_name,
            last_name,
            password
        )

        user.is_staff = True
        user.is_admin = True
        user.is_superuser = True

        user.save()
        return user


class User(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    first_name = models.CharField(max_length=40, blank=True)
    last_name = models.CharField(max_length=40, blank=True)
    username = models.CharField(max_length=40, unique=True, blank=True, editable=False)
    # display_name = models.CharField(max_length=150)
    bio = models.TextField(blank=True, null=True)
    avatar = models.ImageField(blank=True, null=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name','last_name']

    def __str__(self):
        return "{} @{}".format(self.email, self.username)

    def get_short_name(self):
        return self.first_name

    def get_full_name(self):
        return ' '.join([self.first_name, self.last_name])

      

This seems to work fine when registering the superuser from the shell. I have a form and view configured to register regular users on my site like this:

/accounts/forms.py

from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _

auth_code = 'hamburger'

def validate_authorization(value):
    if value != auth_code:
        raise ValidationError(
            _('Must have valid authorization code in order to register.')
        )


class UserCreateForm(UserCreationForm):
    authorization_code = forms.CharField(max_length=10, required=True, validators=[validate_authorization])

    class Meta:
        model = get_user_model()
        fields = ("email", "first_name", "last_name", "password1", "password2", "authorization_code")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["email"].label = "Email Address"
        self.fields["first_name"].label = "First Name"
        self.fields["last_name"].label = "Last Name"
        self.fields["password1"].label = "Password"
        self.fields["password2"].label = "Password Confirmation"
        self.fields["authorization_code"].label = "Authorization Code"

      

/accounts/views.py

from django.shortcuts import render

from django.template import RequestContext
from django.contrib.auth import login, logout
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.core.urlresolvers import reverse_lazy
from django.views import generic
from django.http import HttpResponseRedirect

from django.contrib.auth import get_user_model

from . import forms


class SigninView(generic.FormView):
    form_class = AuthenticationForm
    success_url = '/dashboard/' #reverse_lazy('index')
    template_name = 'accounts/signin.html'

    def get_form(self, form_class=None):
        if form_class is None:
            form_class = self.get_form_class()
        return form_class(self.request, **self.get_form_kwargs())

    def form_valid(self, form):
        login(self.request, form.get_user())
        return super().form_valid(form)


class SignoutView(generic.RedirectView):
    url = '/' #reverse_lazy("home")

    def get(self, request, *args, **kwargs):
        logout(request)
        return super().get(request, *args, **kwargs)


class RegisterView(generic.CreateView):
    form_class = forms.UserCreateForm
    success_url = '/'
    template_name = 'accounts/register.html'

    def form_valid(self, form):
        self.object = form.save(commit=False)
        form.instance.username = ''.join([form.instance.first_name.lower(), form.instance.last_name[:1].lower()])
        i=2
        while get_user_model().objects.filter(username=form.instance.username).exists():
            form.instance.username = ''.join([form.instance.first_name.lower(), form.instance.last_name[:i].lower()])
            i+=1
        form.save()
        return HttpResponseRedirect(self.get_success_url())
        # return super(RegisterView, self).form_valid(form)

      

I don't understand why my superuser cannot log into the site, but my regular users can. Also, you will notice that I have a while statement that automatically generates a username based on the first and last name entered. I originally only had this in the UserManager, but the form was bypassing the custom manager, so I had to add the same block of code to my view. So there is a gap between users created from the form and users created from the shell (UserManager).

authorization_code

in place because I don't want anyone to register on my site and I didn't know a better way. I am open to the best offers.

Additional information that might be helpful

settings.py

# Set user authentication model
AUTH_USER_MODEL = 'accounts.User'

      

Python 3.5, Django 1.10

Thanks in advance for any advice or insight.

0


source to share


1 answer


The problem has been resolved.

IN

def create_superuser(self, email, first_name, last_name, password):
    user = self.create_user(
        email,
        first_name,
        last_name,
        password
    )

      

I forgot to install password=password,

. From looking at the password field in the database, it seems that this also led (as close as possible) to a workaround <algorithm>$<iterations>$<salt>

(per Django docs https://docs.djangoproject.com/en/1.10/topics/auth/passwords/ ), although the password is all was also hashed in some way (not saved in plain text) the password field for superusers was significantly shorter than the password field for regular users. No matter what he did, he did not store the actual password and did not give me the wrong username / password when I tried to log in with the superuser account.



So the correct way is

def create_superuser(self, email, first_name, last_name, password):
    user = self.create_user(
        email,
        first_name,
        last_name,
        password=password,
    )

      

I still don't understand why it created_username

bypasses UserManager

when saving user from AuthenticationForm

, but I found a workaround by adding the same while statement to the view. At least now everything is functional. I'm still curious to know if anyone has any further understanding of this question.

0


source







All Articles