Equivalent of get_or_create for adding users - django

Is there a simpler way to add a user than with the following pattern?
try:
new_user = User.objects.create_user(username, email, password)
except IntegrityError:
messages.info(request, "This user already exists.")
else:
new_user.first_name = first_name
# continue with other things

In Django 1.4, get_or_create() exists for User.
from django.contrib.auth.models import User
_user = User.objects.get_or_create(
username=u'bob',
password=u'bobspassword',
)

It is better not to catch IntegrityError as it can happen for other reasons. You need to check if user exists, excluding the password. If user already exists, set the password.
user, created = User.objects.get_or_create(username=username, email=email)
if created:
user.set_password(password)
user.save()

I don't think so. But you can proxify the Django User:
class MyUser(User):
class Meta:
proxy = True
def get_or_create(self, username, email, password, *args, **kwargs):
try:
new_user = User.objects.create_user(username, email, password)
except IntegrityError:
return User.objects.get(username=username, email=email)
else:
new_user.first_name = kwargs['first_name'] # or what you want
...etc...
return new_user

This method should solve this problem but keep in mind that in the database, password is kept as a hash of the password, and as pointed before, "get_or_create" makes an exact lookup. So before the lookup actually happens, we "pop" the password from the kwargs.
This method on your custom UserManager:
def get_or_create(self, defaults=None, **kwargs):
password = kwargs.pop('password', None)
obj, created = super(UserManager, self).get_or_create(defaults, **kwargs)
if created and password:
obj.set_password(password)
obj.save()
return obj, created

Related

Django authentication with custom model

I have a custom login authentication with mysql table, while logging in how can I compare a hashed password with a plain-password in backends.py (Working fine with plain password)?
class MyBackEnd(object):
def authenticate(self, request, email=None, password=None):
existing_user = RegAuth.objects.get(email=email,password=password)
if not existing_user:
# Checking the user Regauth Custom DB.
user_data = RegAuth.objects.get(email=email,password=password)
if email == user_data.email:
user = RegAuth.objects.create_user(email=email, password=password)
user.save()
return user
else:
return None
else:
return existing_user
def get_user(self, email):
try:
return RegAuth.objects.get(email=email)
except Exception as e:
return False
Login view
def logauth(request):
if request.method == "POST":
email = request.POST['username']
password = request.POST['password']
user = authenticate(request, email=email, password=password)
if user is not None:
messages.error(request, 'if part : user is not None')
login(request, user)
return redirect('emp')
else:
messages.error(request, 'else part : user is None')
return redirect('login_url')
else:
messages.error(request, 'Please provide valid credentials')
return render(request, 'registration/login.html')
Is there any particular reason to diverge from Django's default authentication backend? I see at least a few issues with your authenticate method;
class MyBackEnd(object):
def authenticate(self, request, email=None, password=None):
# 1. password should not be used to retrieve a user, a pk should suffice
existing_user = RegAuth.objects.get(email=email,password=password)
if not existing_user:
# Checking the user Regauth Custom DB.
# 2. if the query before didn't yield results, why would it atp?
user_data = RegAuth.objects.get(email=email,password=password)
if email == user_data.email:
# 3. I'm not sure what flow validates this path, could you explain?
user = RegAuth.objects.create_user(email=email, password=password)
user.save()
return user
else:
return None
else:
return existing_user
For reference here's Django's default authenticate backend method (notice the use of except/else syntax):
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
It;
Tries to retrieve the user, then;
Checks the password, and;
Validates the user's state, then;
Returns the user object to the caller (successful authentication), or
Does NOT return a user to the caller. (failed authentication)
Alternately (no user found), it;
Instantiates a user object, and;
Sets the password using set_password (which hashes it), then;
Does NOT return a user to the caller. (failed authentication)
This alternate flow's usage of set_password is meant to mitigate some timing attacks see: https://code.djangoproject.com/ticket/20760
If you want to register new users, reset a user's password, or anything besides authentication, the authenticate method is not the right place.
I am aware this is not an answer to your question, but I hope it helps you and possibly others to understand the authentication flow and be critical about diverging from it.

Extending AbstractBaseUser not hitting ModelBackend - Django

I'm extending AbstractBaseUser with my custom user model. I can create a superuser via shell successfully with the UserManager() below which is created in the database correctly.
For testing, I've created a superuser with the username test & password of test.
check_password()
def set_password(self, raw_password):
self.password = make_password(raw_password)
self._password = raw_password
def check_password(self, raw_password):
"""
Return a boolean of whether the raw_password was correct. Handles
hashing formats behind the scenes.
"""
def setter(raw_password):
self.set_password(raw_password)
# Password hash upgrades shouldn't be considered password changes.
self._password = None
self.save(update_fields=["password"])
return check_password(raw_password, self.password, setter)
I can run this test user against the check_password("test", "test") method which returns True as expected, but if I try to login via /admin I get "Password Incorrect" with a 200 status code on the POST.
Update: check_password does return False when given the raw password & hash
>>> u = User.objects.get(pk=1)
>>> u.check_password('test')
False
>>> u.check_password('pbkdf2_sha256$150000$sWSs4Yj3gQe1$75A2JmFurNX2oOeKJ18TvsB2G3YU6mYjIuHlaH7i6/k=')
False
Relevant app versions
Django==2.2.3
djangorestframework==3.10.1
User Model
class User(AbstractBaseUser):
USERNAME_FIELD = ('username')
REQUIRED_FIELDS = ('email', 'password')
username = models.CharField(max_length=15, unique=True)
twitch_id = models.IntegerField(null=True)
avatar = models.URLField(null=True, blank=True)
is_live = models.BooleanField(default=False)
email = models.EmailField(unique=True)
password = models.CharField(max_length=50, default="password")
register_date = models.DateTimeField(auto_now=True)
twitch_token = models.ForeignKey(TwitchToken, on_delete=models.SET_NULL, null=True)
twitter_token = models.ForeignKey(TwitterToken, on_delete=models.SET_NULL, null=True)
# attempted giving flags from original User model
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
objects = UserManager()
class Meta:
db_table = 'users_user'
def __str__(self):
return self.username
"""
Properties are redundant with flags above
#property
def is_admin(self):
return self.admin
#property
def is_active(self):
return self.active
#property
def is_staff(self):
return self.staff
#property
def is_superuser(self):
return self.superuser
"""
UserManager()
class UserManager(BaseUserManager):
def create_user(self, username, email, password):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_staffuser(self, username, email, password):
"""
Creates and saves a staff user with the given email and password.
"""
user = self.create_user(
username,
email,
password,
)
user.staff = True
user.save(using=self._db)
return user
def create_superuser(self, username, email, password):
"""
Creates and saves a superuser with the given email and password.
"""
user = self.create_user(
username,
email,
password,
)
user.is_staff = True
user.is_admin = True
user.is_active = True
user.save(using=self._db)
return user
I am explicitly stating to use django.contrib.auth.backends.ModelBackend (default) in my settings & my AUTH_USER_MODEL is set. (I have seen some use a tuple & others use a list. I've tried both, same results)
AUTH_USER_MODEL = 'users.User'
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
I suspect I'm not even hitting ModelBackend 'cause I've put some prints in the authenticate() method that aren't running, as well I have deleted the entire file & see the same results. So I suspect the issue is somewhere between Django's determination of the auth user model & actually attempting authentication.
I've looked through countless SO posts & forum posts and I'm not seeing any step of the extension process that I'm missing, but I can't get anything valuable from stack traces either.
Django ModelBackend for Reference
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
UserModel = get_user_model()
class ModelBackend:
"""
Authenticates against settings.AUTH_USER_MODEL.
"""
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
def user_can_authenticate(self, user):
"""
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_active = getattr(user, True, None)
return is_active or is_active is None
def _get_user_permissions(self, user_obj):
return user_obj.user_permissions.all()
def _get_group_permissions(self, user_obj):
user_groups_field = get_user_model()._meta.get_field('groups')
user_groups_query = 'group__%s' % user_groups_field.related_query_name()
return Permission.objects.filter(**{user_groups_query: user_obj})
def _get_permissions(self, user_obj, obj, from_name):
"""
Return the permissions of `user_obj` from `from_name`. `from_name` can
be either "group" or "user" to return permissions from
`_get_group_permissions` or `_get_user_permissions` respectively.
"""
if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set()
perm_cache_name = '_%s_perm_cache' % from_name
if not hasattr(user_obj, perm_cache_name):
if user_obj.is_superuser:
perms = Permission.objects.all()
else:
perms = getattr(self, '_get_%s_permissions' % from_name)(user_obj)
perms = perms.values_list('content_type__app_label', 'codename').order_by()
setattr(user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms})
return getattr(user_obj, perm_cache_name)
def get_user_permissions(self, user_obj, obj=None):
"""
Return a set of permission strings the user `user_obj` has from their
`user_permissions`.
"""
return self._get_permissions(user_obj, obj, 'user')
def get_group_permissions(self, user_obj, obj=None):
"""
Return a set of permission strings the user `user_obj` has from the
groups they belong.
"""
return self._get_permissions(user_obj, obj, 'group')
def get_all_permissions(self, user_obj, obj=None):
if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set()
if not hasattr(user_obj, '_perm_cache'):
user_obj._perm_cache = {
*self.get_user_permissions(user_obj),
*self.get_group_permissions(user_obj),
}
return user_obj._perm_cache
def has_perm(self, user_obj, perm, obj=None):
return user_obj.is_active and perm in self.get_all_permissions(user_obj, obj)
def has_module_perms(self, user_obj, app_label):
"""
Return True if user_obj has any permissions in the given app_label.
"""
return user_obj.is_active and any(
perm[:perm.index('.')] == app_label
for perm in self.get_all_permissions(user_obj)
)
def get_user(self, user_id):
try:
user = UserModel._default_manager.get(pk=user_id)
except UserModel.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
class AllowAllUsersModelBackend(ModelBackend):
def user_can_authenticate(self, user):
return True
class RemoteUserBackend(ModelBackend):
"""
This backend is to be used in conjunction with the ``RemoteUserMiddleware``
found in the middleware module of this package, and is used when the server
is handling authentication outside of Django.
By default, the ``authenticate`` method creates ``User`` objects for
usernames that don't already exist in the database. Subclasses can disable
this behavior by setting the ``create_unknown_user`` attribute to
``False``.
"""
# Create a User object if not already in the database?
create_unknown_user = True
def authenticate(self, request, remote_user):
"""
The username passed as ``remote_user`` is considered trusted. Return
the ``User`` object with the given username. Create a new ``User``
object if ``create_unknown_user`` is ``True``.
Return None if ``create_unknown_user`` is ``False`` and a ``User``
object with the given username is not found in the database.
"""
if not remote_user:
return
user = None
username = self.clean_username(remote_user)
# Note that this could be accomplished in one try-except clause, but
# instead we use get_or_create when creating unknown users since it has
# built-in safeguards for multiple threads.
if self.create_unknown_user:
user, created = UserModel._default_manager.get_or_create(**{
UserModel.USERNAME_FIELD: username
})
if created:
user = self.configure_user(user)
else:
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
pass
return user if self.user_can_authenticate(user) else None
def clean_username(self, username):
"""
Perform any cleaning on the "username" prior to using it to get or
create the user object. Return the cleaned username.
By default, return the username unchanged.
"""
return username
def configure_user(self, user):
"""
Configure a user after creation and return the updated user.
By default, return the user unmodified.
"""
return user
class AllowAllUsersRemoteUserBackend(RemoteUserBackend):
def user_can_authenticate(self, user):
return True
The ModelBackend.authenticate method first gets the user object from the database using the get_by_natural_key method of the user models default manager, if this fails then authentication will fail
def get_by_natural_key(self, username):
return self.get(**{self.model.USERNAME_FIELD: username})
Because your create_user method is not setting the username field correctly this is failing
The reason why you were able to create the user even though the username field is required is probably because you are using MySQL and running in non-strict mode in which case null values will be converted to empty strings

Using Email as login in Django

I am not able to login in django with correct password and email. here is my code.
backends.py
from django.contrib.auth.models import User
class Emailbackend(object):
def authenticate(self, username=None, password=None, **kwargs):
try:
user = User.objects.get(email=username)
except User.MultipleObjectsReturned:
user = User.objects.filter(email=username).order_by('id').first()
except User.DoesNotExist:
return None
if getattr(user, 'is_active') and user.check_password(password):
return user
return None
def get_user(self, user_id):
try:
User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Settings.py
AUTHENTICATION_BACKENDS = (
'users.backends.Emailbackend',
)
Actually, authenticate method expects an argument request should be passed. So you need to declare your Backend like this:
class Emailbackend(object):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
# rest of the code
Reference can be found here.
first check the value of user in authenticate method try block. If it get correct result then check following block what user value return.
if getattr(user, 'is_active') and user.check_password(password):
return user
If it doesn't work then go to your database and check is your password hash value available or not.
Try these link
link1 link2

update password only if it is entered while updating user in Django admin

I have custom user in Django, so in user creation form in admin we have password field, and its getting saved while creation, but when I go to change existing user and I am not entering/changing password field , but it getting reflected in database. below is my code
class ChangeClientEmployeeMasterForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ChangeClientEmployeeMasterForm, self).__init__(*args, **kwargs)
self.fields['groups'].label='Roles'
if 'instance' in kwargs and hasattr(kwargs['instance'], 'client_employee_id'):
self.client_employee_id = kwargs['instance'].client_employee_id
def clean(self):
if self.cleaned_data['client_employee_type'] =='imast':
self.cleaned_data['is_superuser'] = True
else :
self.cleaned_data['is_superuser'] = False
return self.cleaned_data
def save(self, commit=True):
user = super(ChangeClientEmployeeMasterForm, self).save(commit=False)
password = self.cleaned_data["password"]
if password:
user.set_password(password)
if commit:
user.save()
return user
class Meta:
model = ClientEmployeeMaster
You try this option, I tried this in Django Admin 1.10. This should work.
def clean(self):
password = self.cleaned_data['password']
if not password and password =='':
del self.cleaned_data['password']
#Add your extra code / statement here, if you need.
return self.cleaned_data

Django Register Form 'AnonymousUser' object has no attribute 'backend'

I have the following register view that enters a new user.
I want it to enter the new user and then log in automatically.
It saves through the User record but returns this error when trying to login:
'AnonymousUser' object has no attribute 'backend'
views.py
def register(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST, error_class=DivErrorList)
if form.is_valid():
form.save()
new_user = authenticate(username=request.POST['username'],password=request.POST['password'])
login(request, new_user)
return HttpResponseRedirect('/production/')
else:
form = UserRegisterForm(error_class=DivErrorList)
return render(request,'register.html', {
'form': form,
})
forms.py
class UserRegisterForm(forms.ModelForm):
class Meta:
model = User
fields = ('username','first_name','last_name','email','password')
password_compare = forms.CharField(max_length=128)
def __init__(self, *args, **kwargs):
super(UserRegisterForm, self).__init__(*args, **kwargs)
self.fields['password_compare'].label = 'Password Again'
self.fields['password'].help_text = ''
self.fields['first_name'].label = 'First Name'
self.fields['last_name'].label = 'Last Name'
self.fields['email'].label = 'E-mail Address'
def clean(self):
cleaned_data = self.cleaned_data
password1 = cleaned_data.get('password', None)
password2 = cleaned_data.get('password_compare', None)
if not (password1):
error_msg = u'This field is required.'
self._errors['password'] = self.error_class([error_msg])
if not (password2):
error_msg = u'This field is required.'
self._errors['password_compare'] = self.error_class([error_msg])
# password fields must match
if password1 != password2:
error_msg = u'Password doesn\'t match the confirmation.'
self._errors['password'] = self.error_class([error_msg])
del cleaned_data['password']
# cannot have a username already existing
try:
existing_user = User.objects.get(username=cleaned_data.get('username'))
error_msg = u'Username already exists.'
self._errors['username'] = self.error_class([error_msg])
del cleaned_data['username']
return cleaned_data
except User.DoesNotExist:
return cleaned_data
Your user will never authenticate, because you're saving the password in plain text - and authenticate expects a hashed password. You should call user.set_password(password) on the newly-created user object before saving it to the db - see the built-in UserCreationForm.
I had the same error for a newly registering user and it left me frustrated for an hour.
There was a piece of code that tried to log user in right after the registration.
Usually it worked just fine, but not this time.
def attempt_login(self, email, password):
user = authenticate(username=email, password=password)
login(self.request, user)
return user
It seemed that authenticate returned None, and then calling login with None caused this exception. But I was sure the User has been created after registration.
Finally, I realized that this particular user's login was longer than 30 characters, and the form field had no validation. The login would get truncated in the database, and therefore authenticate was called for non-existent login.