Django: How do I authenticate an AbstractBaseUser? - python

I have created a model for an AbstractBaseUser, but am struggling to authenticate it.
Here is my model:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.contrib.auth.password_validation import validate_password
from uuid import uuid4
class CustomUserManager(BaseUserManager):
def _create_user(self, email, password, is_staff=False, is_superuser=False, **other_fields):
if not email:
raise ValueError('Email address must be specified')
if not password:
raise ValueError('Password must be specified')
user = self.model(
email=self.normalize_email(email),
is_staff=is_staff,
is_superuser=is_superuser,
**other_fields
)
validate_password(password)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password, **other_fields):
return self._create_user(email, password, False, False, **other_fields)
def create_superuser(self, email, password, **other_fields):
return self._create_user(email, password, True, True, **other_fields)
class CustomUser(AbstractBaseUser):
id = models.UUIDField(primary_key=True, default=uuid4(), editable=False, unique=True)
email = models.EmailField(max_length=254, unique=True)
is_superuser = models.BooleanField(default=True)
is_staff = models.BooleanField(default=True)
is_active = models.BooleanField(default=True)
is_premium = models.BooleanField(default=False)
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
display_name = models.CharField(max_length=40)
date_of_birth = models.DateField()
currency = models.CharField(max_length=3)
date_joined = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
REQUIRED_FIELDS = ['first_name', 'display_name', 'date_of_birth', 'currency']
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
objects = CustomUserManager()
def get_full_name(self):
return ("%s %s" % (self.first_name, self.last_name)) if self.last_name != '' else self.first_name
def get_short_name(self):
return self.first_name
def __str__(self):
return "%s %s - %s" % (self.first_name, self.last_name, self.email)
Here is my view:
from django.contrib.auth import authenticate
from django.http import HttpResponse
def user_login(request):
user = authenticate(request, email=request.POST['email'], password=request.POST['password'])
if user is not None:
login(request, user)
else:
HttpResponse("Invalid email/password pair", status=401)
return HttpResponse()
And, in my settings.py, I have the following:
AUTH_USER_MODEL = 'myapp.CustomUser'
The issue is that, whenever I send create a user and send a request to user_login, the authenticate method always returns None, thus triggering the HttpResponseBadRequest. The email in the request matches what is in the database, and the password matches the password I used to create the user (I've spent longer than I care to admit using print statements to verify that).
Is there something I am missing? Do AbstractBaseUsers require something extra for authentication?

The comment by Iain Shelvington clued me into the answer.
The test I was using to test the authentication was creating the user by saving it directly to the database like this:
from .models import CustomUser
user_data = generate_user()
CustomUser(**user_data).save()
Because the user was being saved directly to the database, its password never got hashed.
I needed to instead create the user like this:
from Django.test import Client
from .models import CustomUser
user_data = generate_user()
client = Client()
client.post('/endpoint/used/to/create/user', user_data)

Related

Link Customer Django user to group and give them permission

I customized the Django user model, now I can't link my users to Django groups to give them permissions
from django.db import models
from django.core.validators import RegexValidator
from django.contrib.auth.models import (BaseUserManager, AbstractBaseUser, PermissionsMixin)
from django.db.models.signals import post_save
from django.contrib.auth.models import Group
USERNAME_REGEX = '^[a-zA-Z0-9.+-]*$'
class UserManager(BaseUserManager):
def create_user(self, username, email, password=None):
if not email:
raise ValueError('Users must have an email address')
user = self.model(username=username, email=self.normalize_email(email))
user.set_password(password)
user.save(using=self._db)
if group is not None:
group.user_set.add(user)
return user
# user.password = password # bad - do not do this
def create_superuser(self, username, email, password=None):
user = self.create_user(username, email, password=password)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
if group is not None:
group.user_set.add(user)
return user
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=300, validators=[
RegexValidator(regex=USERNAME_REGEX, message='Username must be alphanumeric or contain numbers',
code='invalid_username')],
unique=True)
email = models.EmailField(max_length=255, unique=True, verbose_name='email address')
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
def __str__(self):
return self.username
def get_short_name(self):
# The user is identified by their email address
return self.username
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

How to Create New User from User_Profile in DRF?

I am creating a cross-platform application in which the users are - Admin, Instructor, and Student. And for registering a new user privilege is only for admin users, so an admin user can add another admin, instructor, and student. I am trying to make nested serializers by which a UserProfile (AdminProfile, InstructorProfile, or StudentProfile) when created also creates User. I am new to Django and DRF. How can this be done?
serializers.py
from rest_framework import serializers
from .models import User, ProfileAdmin
# # This will have GET method for seeing the users according to the userType.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'password', 'user_type']
extra_kwargs = {'password': {'write_only': True}}
class UserProfileSerializer(serializers.ModelSerializer):
user_account = UserSerializer(many=False)
class Meta:
model = ProfileAdmin
fields = ['email', 'first_name', 'last_name', 'address', 'telNum', 'aliasMailID', 'MSteamsID', 'user_account']
def create(self, validated_data):
account_data = validated_data.pop('user_account')
user = ProfileAdmin.objects.create(**validated_data)
User.objects.create(**account_data)
return user
def update(self, instance, validated_data):
account_data = validated_data.pop('user_account')
user_account = instance.user_account
instance.email = validated_data.get('email', instance.email)
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.address = validated_data.get('address', instance.address)
instance.telNum = validated_data.get('telNum', instance.telNum)
instance.aliasMailID = validated_data.get('aliasMailID', instance.aliasMailID)
instance.MSteamsID = validated_data.get('MSteamsID', instance.MSteamsID)
instance.save()
user_account.username = account_data.get('username', user_account.username)
user_account.user_type = account_data.get('user_type', user_account.user_type)
return instance
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser, PermissionsMixin, UserManager, AbstractBaseUser
from django.conf import settings
from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.utils.translation import gettext_lazy as _
class CustomAccountManager(UserManager):
def _create_user(self, username, password, user_type, **extra_fields):
if not username:
raise ValueError(_('The Username must be set'))
if not password:
raise ValueError('The password must be set')
if not user_type:
raise ValueError('The user type must be set')
username = self.model.normalize_username(username)
user = self.model(username=username, user_type=user_type, **extra_fields)
user.set_password(password)
user.save()
return user
def create_user(self, username, password, user_type, **extra_fields):
if user_type == 'STUDENT' or user_type == 'INSTRUCTOR':
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username, password, user_type, **extra_fields)
def create_superuser(self, username, password, user_type, **extra_fields):
# if is_userType == 'Admin'
if user_type == 'ADMIN':
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(username, password, user_type, **extra_fields)
class User(AbstractBaseUser, PermissionsMixin):
USER_TYPE_CHOICES = (
('ADMIN', 'admin'),
('INSTRUCTOR', 'instructor'),
('STUDENT', 'student'),
)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
user_type = models.CharField(max_length=40, choices=USER_TYPE_CHOICES)
username = models.CharField(max_length=30, unique=True)
password = models.CharField(_('password'), max_length=128)
objects = CustomAccountManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['password', 'user_type']
class ProfileAdmin(User, models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.EmailField(verbose_name="email", max_length=85, unique=True)
aliasMailID = models.EmailField(verbose_name="aliasMailID", max_length=85, unique=True, blank=False)
address = models.TextField()
MSteamsID = models.EmailField(verbose_name="MSteamsID", max_length=85, unique=True, blank=False)
telNum = PhoneNumberField(null=False, blank=False, unique=True
views.py
class RegisterUserAPI(generics.GenericAPIView):
serializer_class = UserProfileSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
return Response({
"user": UserProfileSerializer(user, context=self.get_serializer_context()).data,
})
urls.py
from django.conf.urls import url, include
from django.urls import path
from .views import RegisterUserAPI
urlpatterns = [
path('users/', RegisterUserAPI.as_view(), name="userRegister"),
]
When I run it, I have access to http://localhost:8000/api/users and can see the form to fill the required field, but as soon as I hit POST I get a TypeError:
TypeError at /api/users/
User() got an unexpected keyword argument 'user'
Request Method: POST
Request URL: http://127.0.0.1:8000/api/users/
Django Version: 3.0
Exception Type: TypeError
Exception Value:
User() got an unexpected keyword argument 'user'
Exception Location: C:\PyCharm\MobileLearningApplication\venv\lib\site-packages\django\db\models\base.py in __init__, line 500
Python Executable: C:\PyCharm\MobileLearningApplication\venv\Scripts\python.exe
Python Version: 3.8.9
Python Path:
['C:\\PyCharm\\MobileLearningApplication\\MobileLearningApplication',
'C:\\Python\\python38.zip',
'C:\\Python\\DLLs',
'C:\\Python\\lib',
'C:\\Python',
'C:\\PyCharm\\MobileLearningApplication\\venv',
'C:\\PyCharm\\MobileLearningApplication\\venv\\lib\\site-packages']
Server time: Sat, 9 Oct 2021 09:37:44 +0000

Django Manager isn't available

AttributeError at /register/
Manager isn't available; 'auth.User' has been swapped for 'accounts.Account'
I'm trying to create a registration form but getting the AttributeError.
How can to avoid the error?
Here is the code in of my custom model in
models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, password=None):
if not email:
raise ValueError('Users must have an email address')
if not username:
raise ValueError('Users must have a username')
user = self.model(
email=self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
user = self.create_user(
email=self.normalize_email(email),
password=password,
username=username,
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
about = models.TextField(('about'), max_length=500, blank=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = MyAccountManager()
def __str__(self):
return self.email
# For checking permissions. to keep it simple all admin have ALL permissons
def has_perm(self, perm, obj=None):
return self.is_admin
# Does this user have permission to view this app? (ALWAYS YES FOR SIMPLICITY)
def has_module_perms(self, app_label):
return True
views.py
from django.shortcuts import render
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from .forms import *
from django.contrib.auth import get_user_model
def register_view(request, *args, **kwargs):
form = CreateUserForm(request.POST or None)
if form.is_valid():
user = form.save(commit=True)
user.set_password(form.cleaned_data.get('password1'))
context = {
'form':form,
'btn_label':'Register',
'title':'Register'
}
return render(request, 'auth/auth.html', context)
I updated the setting.py file and set the following link to the Account model in accounts app
settings.py
AUTH_USER_MODEL = 'accounts.Account'
Traceback
Exception Type: AttributeError at /register/
Exception Value: Manager isn't available; 'auth.User' has been swapped for 'accounts.Account'

"AnonymousUser" Error When Non-Admin Users Log In - Django

When I try to login users registered through my AbstractBaseUser model I get the error:
'AnonymousUser' object has no attribute '_meta'
Which highlights the code:
login(request, user)
However, if the user is an admin there is no problem, leaving me to believe that the problem isn't with the 'login_view', but a problem with how the user is tagged (so to speak) when they are registered with AbstractBaseUser.
Any help would be much appreciated!
Here is my code:
Models.py
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager, PermissionsMixin
)
class UserManager(BaseUserManager):
def create_user(self, first_name, last_name, email, password=None, is_active=True, is_staff=False, is_admin=False):
if not first_name:
raise ValueError("Users must enter their first name")
if not last_name:
raise ValueError("Users must enter their last name")
if not email:
raise ValueError("Users must enter an email")
if not password:
raise ValueError("Users must enter a password")
user_obj = self.model(
first_name = first_name,
last_name = last_name,
email = self.normalize_email(email),
)
user_obj.set_password(password) #set and change password?
user_obj.admin = is_admin
user_obj.staff = is_staff
user_obj.active = is_active
user_obj.save(using=self._db)
return user_obj
def create_superuser(self, first_name, last_name, email, password=None):
user = self.create_user(
first_name,
last_name,
email,
password=password,
is_staff=True,
is_admin=True
)
return user
class User(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255, blank=True, null=True)
email = models.EmailField(max_length=255, unique=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
active = models.BooleanField(default=True) #can login
staff = models.BooleanField(default=False) #staff not superuser
admin = models.BooleanField(default=False) #superuser
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name']
objects = UserManager()
def __str__(self):
return self.email
def get_first_name(self):
return self.email
def get_last_name(self):
return self.email
def get_short_name(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
return self.staff
#property
def is_admin(self):
return self.admin
#property
def is_active(self):
return self.active
Forms.py
from django import forms
from django.contrib.auth import (
authenticate,
get_user_model,
login,
logout,
)
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.models import User, Group
User = get_user_model()
class UserAdminCreationForm(forms.ModelForm):
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
def save(self, commit=True):
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserAdminChangeForm(forms.ModelForm):
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('email', 'first_name', 'last_name', 'password', 'admin')
def clean_password(self):
return self.initial["password"]
class LoginForm(forms.Form):
username = forms.CharField(label='Email')
password = forms.CharField(widget=forms.PasswordInput)
Views.py
from django.shortcuts import render
from django.contrib.auth.models import User, Group
from django.contrib.auth import (
authenticate,
get_user_model,
login,
logout,
)
from django.shortcuts import redirect
from .forms import UserAdminCreationForm, LoginForm
def register_view(request):
form = UserAdminCreationForm(request.POST or None)
if form.is_valid():
user = form.save(commit=False)
password = form.cleaned_data.get('password')
user.set_password(password)
user.save()
user.groups.add(Group.objects.get(name='customers'))
login(request, user)
context = {
"form": form,
}
return render (request, "register.html", context)
def login_view(request):
form = LoginForm(request.POST or None)
if form.is_valid():
username = form.cleaned_data.get("username")
password = form.cleaned_data.get("password")
user = authenticate(username=username, password=password)
login(request, user)
context = {
"form": form,
}
return render (request, "login.html", context)
Try using the methods that the BaseClass provides. That will prevent you from making unexpected errors.
You can use super() for that purpose.

Django - Trouble adding User to User Group with custom User Model

Previously, I have been using the default Django user model and have been adding users to specific user groups when they register with no problem using this code in my views.py:
user.save()
user.groups.add(Group.objects.get(name='customers'))
However, I have now changed to a custom user model so I can remove the 'username' field in my register form and now this code no longer works. This error message is being thrown when new users try to register:
'User' object has no attribute 'groups'
I have searched but can't find an answer to this question. I am very new to working on the backend with Django so please be very descriptive in your answers (i.e where I need to put the code you suggest, models.py/views.py/forms.py etc). Any help would be much appreciated!
My models.py:
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager
)
class UserManager(BaseUserManager):
def create_user(self, email, first_name, last_name, password=None, is_staff=False, is_admin=False):
if not email:
raise ValueError("Users must enter an email")
if not password:
raise ValueError("Users must enter a password")
if not first_name:
raise ValueError("Users must enter their first name")
if not last_name:
raise ValueError("Users must enter their last name")
user_obj = self.model(
email = self.normalize_email(email),
first_name = first_name,
last_name = last_name,
)
user_obj.set_password(password) #set and change password?
user_obj.admin = is_admin
user_obj.staff = is_staff
user_obj.save(using=self._db)
return user_obj
def create_superuser(self, email, first_name, last_name, password=None):
user = self.create_user(
email,
first_name,
last_name,
password=password,
is_staff=True,
is_admin=True
)
return user
class User(AbstractBaseUser):
first_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255, blank=True, null=True)
email = models.EmailField(max_length=255, unique=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
# active = models.BooleanField(default=True) #can login
staff = models.BooleanField(default=False) #staff not superuser
admin = models.BooleanField(default=False) #superuser
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name']
objects = UserManager()
def __str__(self):
return self.email
def get_first_name(self):
return self.email
def get_last_name(self):
return self.email
def get_short_name(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
return self.staff
#property
def is_admin(self):
return self.admin
formy.py
from django import forms
from django.contrib.auth import (
authenticate,
get_user_model,
login,
logout,
)
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.models import User, Group
User = get_user_model()
class UserAdminCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name')
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
views.py
from __future__ import unicode_literals
from barbers.forms import BarberProfileForm
from barbers.models import BarberProfile
from django.contrib.auth.models import User, Group
from django.contrib.auth import (
authenticate,
get_user_model,
login,
logout,
)
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import redirect, render, get_object_or_404
from .forms import BarberRegisterForm, UserLoginForm, UserRegisterForm, UserAdminCreationForm
def register_view(request):
print(request.user.is_authenticated())
register_title = "Register"
register_form = UserAdminCreationForm(request.POST or None)
if register_form.is_valid():
user = register_form.save(commit=False)
password = register_form.cleaned_data.get('password')
user.set_password(password)
user.save()
user.groups.add(Group.objects.get(name='customers'))
new_user = authenticate(username=user.username, password=password)
login(request, new_user)
if next:
return redirect(next)
return redirect("/my-barbers/{0}".format(request.user.id), user=request.user)
context = {
"register_form": register_form,
"register_title": register_title,
}
return render (request, "login.html", context)
What's your custom job user model?
Maybe you inherit AbstractUser or AbstractBaseUser.
But when you need to use groups, ou have to inherit PermissionsMixin
from django.contrib.auth.models import PermissionMixin
you can check in django github code here
here's part of the code
class PermissionsMixin(models.Model):
"""
Add the fields and methods necessary to support the Group and Permission
models using the ModelBackend.
"""
is_superuser = models.BooleanField(
_('superuser status'),
default=False,
help_text=_(
'Designates that this user has all permissions without '
'explicitly assigning them.'
),
)
groups = models.ManyToManyField(
Group,
verbose_name=_('groups'),
blank=True,
help_text=_(
'The groups this user belongs to. A user will get all permissions '
'granted to each of their groups.'
),
related_name="user_set",
related_query_name="user",
)
Hope solving well!
Adding
This is my part of user model. You should inherit both BaseUser and PermissionsMixin. Then you can use groups from PermissionsMixin.
class JobUser(AbstractBaseUser, PermissionsMixin, TimeStampedModel):
email = models.EmailField(
verbose_name="Email",
max_length=255,
unique=True,
db_index=True,
)
user_type = models.PositiveIntegerField(
verbose_name="User type",
# 0 - staff
# 1 - employer
# 2 - employee
choices=CHOICES.USER_CHOICES,
default=2,
)
...
So your User model will be like...
class User(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255, blank=True, null=True)
email = models.EmailField(max_length=255, unique=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
# active = models.BooleanField(default=True) #can login
staff = models.BooleanField(default=False) #staff not superuser
admin = models.BooleanField(default=False) #superuser
...
Also don't forget import PermissionMixin in top of your code.
Hope solving well!

Categories

Resources