add auth validation in Django - django

I have a django application and I want to add my own auth validation and check if the user is expired (check the expiration date from some of my models). I want to raise a ValidationError in the login page with appropriate message if user is expired. What's the best way to do that?
Thanks, Alex

If you REALLY want to do your own custom authentication, you should read custom backends in the Django Documentation.
You probably don't want to do your own though. It sucks. Really. Unless there is a really really good reason, you should avoid your own authentication. The main reason being, that many django apps stop working if you don't use the built in User model. If you need to authenticate against an existing source, that's a valid reason for creating your own backend. But there are pitfalls, and you still probably want to use the built in User model for your custom backend.
You should tell us why you want to do your own custom authentication, and perhaps we can help you achieve your requirement, without writing a custom backend.
Edit
Ok, I think I understand what you mean now. What (I think) you want, is a custom authentication form. We currently use custom form (though we have a different unavoidable backend), so you should be able to use the following quite easily.
from django.contrib.auth.forms import AuthenticationForm
from django import forms
from myproject.myapp.models import MyClass
class CustomAuthForm(AuthenticationForm):
def clean(self):
cleaned_data = super(CustomAuthForm, self).clean()
user = self.user_cache # set by super class
if user.my_class.expired:
raise forms.ValidationError('This User has Expired!')
return cleaned_data
Then, to use this custom authentication form, you need a URL in your urls.py:
from myproject.myapp.forms import CustomAuthForm
url(r'^login/$', 'django.contrib.auth.views.login', name='login',
kwargs={'template_name':'youproject/login.html', 'authentication_form':CustomAuthForm}),
I see now that your question originally stated you wanted custom validation, not authentication. My apology for not reading your question correctly.

Related

How can I set up site-wide password protection and privilege requirements on views with Django?

My Django app is meant for internal use at the company I work at; however, it needs to be set up such that only certain employees with login credentials can access any of the web pages. Furthermore, I need to be able to customize the views so that only accounts with the right privileges can access them. How would I go about this?
My gut instinct is to use the same password protection and privilege system that the admin site uses since my project is basically just meant for viewing and editing the database easily. However, Google doesn't seem to turn up any information on how to do this, and I've only been working with Django for a month and half so I don't quite have the chops to figure this out myself. Is there a way to use the admin site's password protection and privilege system or is it inaccessible?
If staff privileges are enough this can easily be done using the staff_member_required view decorator. All you need is to apply the decorator to your view
staff_member_required Example:
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
def random_view(request):
#If the user does not have staff privileges he will be redirected to a login page
If you are looking to test more specific privileges you should use the user_passes_test decorator. See: Django User Passes Test Decorator
You apply the decorator to your view and pass a function as a parameter that validates if a user should have access to that page, if the function returns True access is given normally, otherwise the user will be redirected.
user_passes_test Example:
from django.contrib.auth.decorators import user_passes_test
def user_is_author(user)
#your test goes here, pseudo code example ignore syntax.
return user.is_author
#user_passes_test(user_is_author)
def random_view(request):
#if user passed the test the view will work normally

Django REST - Serialize User.get_all_permissions

I am building an application with a Django Rest backend, and a VueJS front end and am working through authorization and authentication. I have the authentication working well, but am a bit stuck on letting the front end (VueJS) know what the user has authorization to do in terms of Add/Change/View/Delete for a model. For example, if a user cannot add a customer, I don't want to show the 'Add Customer button'.
Working through the Django docs, and solutions on StackOverflow, I believe the simplest way is to send the user's permissions from Django to VueJS.
The 'best'/'simplest' way I can see to get the permissions is with the following:
userModel = User.objects.get(request.user)
return User.get_all_permissions(userModel)
Where I am stuck is exactly where to put this logic and how to serialize it. Does the above belong in the View, Serializer, other? Up until now, I have only been working with Models (ModelSerializers and ModelViews), but I don't believe this falls into this category.
Thanks in advance...
You should add this logic to views, because the views are used to implement these kinds of logic.
Actually, you don't want to use serializers here, because of the response of .get_all_permissions() method is already in serialized form
Apart from that, your provided code is not good (it's clearly bad). It should be as below,
return request.user.get_all_permissions()
because, you'll get current logged-in user's instance through request.user, to get his/her permissions, you all need to call the get_all_permissions() method
Example
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
#permission_classes(IsAuthenticated, )
#api_view()
def my_view(request):
logged_in_user = request.user
return Response(data=logged_in_user.get_all_permissions())

Django-allauth with multiple profile models

I have a django project in which there are multiple profile models, each having a foreign key to the User model. It uses django-allauth for registration.
Currently, when registering using a social account, the user registers, a User and a socialaccount is created, then the user is redirected to a form to fill out, depending on what profile type s/he chose earlier, and after filling out that form is the correct profile type created.
I'd like it if the user, socialaccount and profile type instances are created in the same step, that is after the user fills out the profile specific form. Is there anyway I can do this without changing allauth's code? It wouldn't be too hard to do this by modifying allauth, but I'd rather not maintain a custom copy of a 3rd party app if it can be helped.
Using a custom adapter is out, because it does not have access to the request.
Take a look at the allauth.account.signals.user_signed_up signal, which is triggered just after the user (social or normal) signs up. You can do your stuff with your account there:
from django.dispatch import receiver
from allauth.account.signals import user_signed_up
#receiver(user_signed_up)
def do_stuff_after_sign_up(sender, **kwargs):
request = kwargs['request']
user = kwargs['user']
# Do your stuff with the user
user.save()
From django-allauth repo:
# Typically followed by `user_logged_in` (unless, e-mail verification kicks in)
user_signed_up = Signal(providing_args=["request", "user"])
To learn how to use signals in django, read the official documentation about it.
I hope it helps you!
EDIT
I'm glad you found your way through your problem, but since middlewares are loaded always, I would suggest using the approach I proposed. The socialaccount module from django-allauth also provide signals. Among them, you can find the allauth.socialaccount.signals.social_account_added:
# Sent after a user connects a social account to a his local account.
social_account_added = Signal(providing_args=["request", "sociallogin"])
With a handler similar to the previously written, you can check the user model for the required fields, and then call the redirect shortcut, or return an HttpResponseRedirect object that redirects to the view that shows/handle your form.

Django: How to destroy user session after a password reset/change?

i've recently implemented a simple change password view in my django project. The thing is that the old session should be destroyed for security reasons. What's the best way of doing this without asking the user to log in again.
I think i could just logout/login him/her, something like this:
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
#login_required
def change_password(request):
# My stuff
request.user.set_password(new_password)
request.user.save()
# I need this:
logout(request)
login(request,request.user)
But i think this is not the best idea. What do you think?
Is there another way to do this?
Am I missing something? (I mean, is this secure)
Take a look at this app https://github.com/atugushev/django-password-session.
This package makes invalidated all sessions (except a current session) after change a password.
Also this feature finally was implemented in Django 1.7. See: https://docs.djangoproject.com/en/dev/topics/auth/default/#session-invalidation-on-password-change
I just found out that this is now a built-in feature of Django, and has been since 1.7:
https://docs.djangoproject.com/en/1.7/topics/auth/default/#session-invalidation-on-password-change
Essentially, all sessions now include a hash of the users' password, so if the user ever changes their password, all their existing sessions are automatically invalidated.
So, the short answer to your question is: upgrade django.
One possibly undesirable side effect of this change is that, by default, a user ends up having to log in again as soon as they change their password. So you probably actually want the current user session to stay logged in. See the docs already linked, Django's built-in views for password change do that for you default, or you can manually call a function called update_session_auth_hash
django clears the session on logout so you will be fine:
https://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.logout
When you call logout(), the session data for the current request is completely cleaned out. All existing data is removed. This is to prevent another person from using the same Web browser to log in and have access to the previous user's session data.
I don't understand whats are these security reasons that forces to reset session. But, the way is:
#login_required
def change_password(request):
request.user.set_password(new_password)
request.user.save()
username = request.user.username
logout(request)
user = authenticate(username=username, password=new_password) #<-- here!!
if user is not None:
login(request,user)
else:
#raise your exception
you should authenticate before login. Quoting doc:
Calling authenticate() first When you're manually logging a user in,
you must call authenticate() before you call login(). authenticate()
sets an attribute on the User noting which authentication backend
successfully authenticated that user (see the backends documentation
for details), and this information is needed later during the login
process.

django.contrib.auth: how to keep site and admin sessions separate?

I'm using a user profile model with a ForeignKey to the User, authenticated by django.contrib.auth.
The same auth module is of course used for the admin interface, so if a superuser/staff member is logged in to the admin interface, and enters the main site, the site will accept session cookie and authenticate him. This creates a problem because a superuser/admin doesn't need to have a UserProfile and shouldn't be recognized by the main site.
What's the easiest way to solve this, so that the sessions from admin don't carry on to the site?
I dont think there is a way to solve exactly this,
"What's the easiest way to solve this, so that the sessions from admin don't carry on to the site?"
But depending on what you wnat to do, you may try,
don't create UserProfile for superuser
if request.user.is_superuser():
UserProf.objects.create(...)
I always have the problem where I want to keep a logged in Admin user and a logged in normal user, simultaneously, when I am developing. To do this, I have two entries in /etc/hosts
127.0.0.1 uswaretech.tld
127.0.0.1 admin.uswaretech.tld
Now, normal user always logs in via uswaretech.tld and admin always via admin.uswaretech.tld so thy can both be logge din simultaneously.
From a design standpoint your idea seems like a bit of a hack, but if you really want to do this you may use a middleware.
class MyMiddleware(object):
def process_request(self, request):
if request.user.is_authenticated:
try:
UserProfile.objects.get(user=request.user)
except UserProfile.DoesNotExist:
from django.contrib.auth.models import AnonymousUser
request.user = request._cached_user = AnonymousUser()
return None
This should be at the top of the middleware list to prevent possible side-effects.
if request.session.get ('has_account', False):
return HttpResponse ("You have no account, sorry.")
Then make sure, every user of your front-end gets, if his session is initiated, the value has_account set properly.