Django - Create user crashes - python

I want to add user in a Django app. here's my code, it fires an exception in this line
user = User.objects.create(userName, userMail, userPass)
Here's the whole code:
def createUser(request):
userName = request.REQUEST.get('username', None)
userPass = request.REQUEST.get('password', None)
userMail = request.REQUEST.get('email', None)
# TODO: check if already existed
user = User.objects.create(userName, userMail, userPass)
user.save()
return render_to_response('home.html', context_instance=RequestContext(request))
Any help?

Use the create_user helper function instead of the create method. It takes care of hashing the password for you, amongst other things.
user = User.objects.create_user(userName, userMail, userPass)
As an aside, fetching the values out of the request data dictionary isn't best practice. It's a good idea to learn about django forms and model forms, which will validate input data for you. Once you understand that, there's a UserCreationForm included with the django auth app which you could use.

Related

Django not logging in properly when custom backend and models are used

I'm trying to implement a custom authentication backend in Django but its behaving in a very strange way. The custom backend authenticate against a different model from the usual Django User model an upon successfully verifying the provided credentials the backend get or create our usual Django User instance which it returns. Login nevertheless doesn't work.
From the documentation I learnt that I just needed to inherit the django.contrib.auth.backends.BaseBackend and override the authenticate() method which I did. As I've mentioned the custom authenticate() method basically verifies a set of credentials (username and password) against ones stored in the database (custom model, not django.contrib.auth.models.User) which if matched it would get or create an instance of django.contrib.auth.models.User via a proxy model which I named Profile; basically the Profile model has a reference of both django.contrib.auth.models.User and my custom model. When logging in though I keep redirected to the login page (It's like Django logs me in but doesn't set something somewhere such that when I try accessing a protected resource I'm redirected back to login). Also when I login which a django.contrib.auth.models.User object it works just fine and I can access the protected pages. Following are the reasons I opted for this authentication approach.
I'm working with an existing database that has it's own User tables with very different schema than what Django provides.(Actually the only fields in this system User table similar to Django's are username, and password)
I utilized Django's inspectb management command to recreate the models of which I'm under strict instructions to leave unmanaged, you know, manage=False in the model's meta class. Basically I can't inherit Django's AbstractUser as it would require new fields to be added.
Thus the best approach I could think of was to use a proxy model that would link both django.contrib.auth.models.User and my custom unmanaged models with a custom Authentication Backend.
I've tried watching for certain variables such as the request.session dictionary and request.user all which are set but still can't login successfully. Also when I use credentials that are not in either of the two models I get a Invalid Credentials message in the login page (desirable behavior).
Actually, my custom authenticate() method works fine and returns a valid user, the issue I think lies in django.contrib.auth.login. What could be the problem?
Here is the my authenticate method
def authenticate(self, request, username=None, password=None):
try:
c_user = CustomUser.objects.get(username=username)
except CustomUser.DoesNotExist:
return None
#pwd_valid = check_password(password, user.password)
if not c_user.password==password:
return None
#Get and return the django user object
try:
profile = Profile.objects.get(custom_user=c_user)
user = profile.user
#Create user if profile has none
if not user:
user = User.objects.create(
username=''.join(secrets.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(24))
)
profile.user = user
profile.save()
#Create new profile if none exists
except Profile.DoesNotExist:
#Create new user for the profile
user = User.objects.create(
username=''.join(secrets.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(24))
)
Profile.objects.create(
user = user,
custom_user = c_user
)
return user
Here is how I added the custom backend to settings.py
AUTHENTICATION_BACKENDS = [
'Authentication.custom.CustomAuth',
'django.contrib.auth.backends.ModelBackend'
]
Can't believe I'm answering my own question. After much debugging and reading documentation I realized I didn't override get_user method of the django.contrib.auth.backends.BaseBackend thus the default one which returns None was always called. I implemented it and it worked like a charm.

How to make a login view for django custom user?

I have made a custom user model using the AbstractUser , removed the username and replaced it with email and extended the model, tried creating superuser and it worked and also created some users by a signup form , logged in the admin interface and it worked however when tried to create a login form for the users it fails
I tried this but it didn't work
def LoginView(request):
if request.method == 'POST':
form = AuthenticationForm(data=request.POST)
if form.is_valid():
user = form.get_user()
login(request,user)
return redirect('accounts:login')
else:
form = AuthenticationForm()
return render(request,'accounts/login.html', {'form':form})
then i tried this
class LoginView(FormView):
form_class = AuthenticationForm
template_name = 'login.html'
def form_valid(self, form):
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = authenticate(email=email, password=password)
# Check here if the user is an admin
if user is not None and user.is_active:
login(self.request, user)
return HttpResponseRedirect(self.success_url)
else:
return self.form_invalid(form)
Obviously i expect the user to be logged in
i think the code in this post is badly formatted. mainly it's my fault as i'm new to this platform
I've developed almost the same setup that you're describing (I didn't remove the username field, just stopped using it). If you haven't already seen it, Django's documentation at https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#substituting-a-custom-user-model is quite helpful.
There are a couple of important things that need to be set up correctly for this to work.
The USERNAME_FIELD on your model should be set to the name of your email field.
The AUTH_USER_MODEL needs to point to your custom user model.
class MyUser(AbstractUser):
USERNAME_FIELD = 'email'
AUTH_USER_MODEL = 'customauth.MyUser'
Since you've removed the username field altogether you might need to subclass django.contrib.auth.forms.AuthenticationForm and django.contrib.auth.views.LoginView to avoid breaking things, but Django should handle a different authentication field quite well.
If you do wind up needing to subclass the view, https://ccbv.co.uk/projects/Django/2.2/django.contrib.auth.views/LoginView/ is a great place to look over all the methods to see what's going on.
Edit - On Subclassing and it's necessity
What I was saying about possibly needing to subclass certain things was influenced by https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#writing-a-manager-for-a-custom-user-model. I wasn't sure if there were other parts of the authentication system that would need you to customize them because you removed the username field.
I've read through some of the source code for Django's authentication system. Here's the path that's being followed.
When the POST request is made to Django's authentication view the authentication form is validated. https://github.com/django/django/blob/2.2.2/django/contrib/auth/forms.py#L191
The authenticate function is called. This iterates through the backends set up and tries to authenticate on each of them. https://github.com/django/django/blob/2.2.2/django/contrib/auth/__init__.py#L62
Django's built-in authentication backend gets the user if it exists using the natural key. https://github.com/django/django/blob/2.2.2/django/contrib/auth/backends.py#L16
We can see in the base manager that the natural key used is the field named by USERNAME_FIELD. https://github.com/django/django/blob/2.2.2/django/contrib/auth/base_user.py#L43
If the form is valid, meaning that the user is authenticated properly, the user is then logged in. https://github.com/django/django/blob/2.2.2/django/contrib/auth/views.py#L88
My reaction is that it looks like Django should work out of the box for your use case. You shouldn't need to write a backend. Here's the extent of the code my gut says you should have to write.
from django.contrib.auth import views as auth_views
from django.shortcuts import resolve_url
class LoginView(auth_views.LoginView):
template_name = 'accounts/login.html'
def get_success_url(self):
return resolve_url('accounts:login')
I don't know if this could be of any use to somebody but I can confirm that Django can authenticate you well with its own Login view if you just replace the username with an email field on your custom user model (as long as you specify the USERNAME_FIELD on the custom user model and are indeed using it by declaring it in the settings).
As I was expecting this behavior I designed a custom HTML form with email/password inputs and used the same principles I would use with the original user model authentication. It was failing though and I understood it was because I wasn't adapting my form to the original Login view expectations or would have worked from the start.
Just make sure to remember that the form HTML input tag for the email address needs to have "type" set to "email" but "id" set to "id_username" and "name" to "username".
This means you can just replace username with an email field and authenticate normally. I did not even declare a Login view for my login form to work, the Django view automacally used from the core is just being called at /accounts/login and working on its own. I'm working on Django 3.2

Django not REST - API user registration

I have my first project as junior in my work. It is old (Django 1.8) and it is normal django framework... not REST.
It supports web, and mobile.
I have to create endpoint for mobile to create user.
I think it is not a problem (to create) but I want to make sure it will be save.
First of all I thought that I will create normal ModelForm (RegisterAPIForm based on model=User with full validation (I mean init all fields that are in "backend" not visible for user | cleaned_data for all fields | special overwriten method save() that in addition hashes password, and send email) and in Views I'll add something like this:
class RegistrationAPITestView(View):
def post(self, request):
form = RegistrationAPIForm(
request.POST
)
if form.is_valid():
form.save()
return JsonResponse({})
else:
#check errors and send error code back
Or I should do it other way, by using User object?
class RegistrationAPITestView(View):
def post(self, request):
#check if user does not exist
#password1 and password2 validation
user = User.objects.create()
user.name = request.POST['username']
user.set_password(request.POST['password'])
#init all fields that user can't choose like groups etc
user.save()
What do you think? Do I need ModelForm that I won't even render? It seems to be safer, but maybe I should check it other way? with User object?
Btw. Registration form already exists for web but there is a lot of "web" stuff that I don't need and I don't have to check and there is another method of saving password, so I believe I should create new one.
My code will be revieved in 2 weeks (senior vacations) but now I'm alone and want to do my best.
There is nothing wrong with the second option, but here is the problem that you as a junior should avoid. This line will make the server return a 500 error request.POST['username'] because python will throw a key error if the user doesn't provide the username, to fix just change to request.POST.get('username', 'value if doesn\'t exit') also make sure that everything is ready before create the user or you will have records in the database that wont be useful. Call validators to the password too and try to cover all possible scenario. Remember never trust the user

Django - Using multiple models for authentication

I am kind of new to django and I am concerned about can we use multiple models for authentication for different types of users for our application e.g for Customers use Customer model, for Suppliers use Supplier model and also keep default User registration model for administration use only? If so can you point me in the right direction, how it can be done? Which package should be used?
There is one way that I came around is by adding a foreign key to each model viz needed for authentication but that will involve joins in every query which could result in performance issues. I need to know if there is a better way. An also these custom models can benefit for all permissions stuff available in admin panel.
Expert opinion will be really appreciated.
There are a few different options to handle that.
Maybe check first the Django-docu.
I've you'ld like to customize it to authenticate your users with a mail-address, here is an example written by knbk and rahul-gupta on the ex-documentation:
Django's default authentication works on username and password fields. Email authentication backend will authenticate users based on email and password.
from django.contrib.auth import get_user_model
class EmailBackend(object):
"""
Custom Email Backend to perform authentication via email
"""
def authenticate(self, username=None, password=None):
user_model = get_user_model()
try:
user = user_model.objects.get(email=username)
if user.check_password(password): # check valid password
return user # return user to be authenticated
except user_model.DoesNotExist: # no matching user exists
return None
def get_user(self, user_id):
user_model = get_user_model()
try:
return user_model.objects.get(pk=user_id)
except user_model.DoesNotExist:
return None
Add this authentication backend to the AUTHENTICATION_BACKENDS setting.
# settings.py
AUTHENTICATION_BACKENDS = (
'my_app.backends.EmailBackend',
...
)

How to disconnect update_last_login?

I implemented my own User class from scratch in Django. But when I log in I have this error:
The following fields do not exist in this model or are m2m fields: last_login
I really don't want the field last_login.
I do some reasearch and the problem is here: contrib.aut.models.py
def update_last_login(sender, user, **kwargs):
"""
A signal receiver which updates the last_login date for
the user logging in.
"""
user.last_login = timezone.now()
user.save(update_fields=['last_login'])
user_logged_in.connect(update_last_login)
I found a workaround but it's not an ellegant solution. I added user_logged_in.disconnect(update_last_login) in my models.py file, where my User class is defined.
Is there any better solution for this?
Not sure if this is related to a newer version of django or what, but in my case
user_logged_in.disconnect(update_last_login)
didn't work. This is what works for me (django 2.1):
user_logged_in.disconnect(update_last_login, dispatch_uid='update_last_login')
Currently in Django 1.7...
I think the workaround you defined is the only valid solution (besides from a monkey patch) currently when using the Django auth login() method. I'm just going to assume you are using the standard login() method which is raising this exception.
If we take a look at the source for the login method, we find at the end of the method, a call to execute user_logged_in.send(sender=user.__class__, request=request, user=user). We can't prevent this signal from executing besides from disconnecting it as you have pointed out.
Alternatively, we could monkey patch the login() method to remove that signal call.
from django.contrib.auth import login
def monkey_patch_login(request, user):
"""
Persist a user id and a backend in the request. This way a user doesn't
have to reauthenticate on every request. Note that data set during
the anonymous session is retained when the user logs in.
"""
session_auth_hash = ''
if user is None:
user = request.user
if hasattr(user, 'get_session_auth_hash'):
session_auth_hash = user.get_session_auth_hash()
if SESSION_KEY in request.session:
if _get_user_session_key(request) != user.pk or (
session_auth_hash and
request.session.get(HASH_SESSION_KEY) != session_auth_hash):
# To avoid reusing another user's session, create a new, empty
# session if the existing session corresponds to a different
# authenticated user.
request.session.flush()
else:
request.session.cycle_key()
request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
request.session[BACKEND_SESSION_KEY] = user.backend
request.session[HASH_SESSION_KEY] = session_auth_hash
if hasattr(request, 'user'):
request.user = user
rotate_token(request)
login = monkey_patch_login
We would put the monkey patch code at the top of the file that needs to call the login() method.

Categories