How to execute code after authentication in Django? - django

I want to execute one or more functions after a user logs into my site. How is this possible? I looked into Middleware. Djangobook says that I'll need this to run a piece of code on each and every request that Django handles. However, I just need the code run when the authentication happens successfully.
Note: I am using Django Allauth for authentication and I don't have any view of my own to log in users.

You need to tap into Allauth's signals. Specifically the user logged in signal
allauth.account.signals.user_logged_in(request, user)
Sent when a user logs in.
So add code similar to the following in your project.
from django.dispatch.dispatcher import receiver
from allauth.account.signals import user_logged_in
#receiver(user_logged_in, dispatch_uid="unique")
def user_logged_in_(request, user, **kwargs):
print request.user
This code should be in a place that's likely to be read when django starts up. models.py and views.py are good candidates.

As per the official documentation, there is a signal allauth.account.signals.user_logged_in which gets triggered when a user logs in. This can serve your purpose.

Related

How can I trigger a function when a user registers in django?

I took over a django web app and am still relatively new to django. I want to trigger a function whenever a new user registers on the website. But there is no code in the project to process the user registration. It all seems to happen under the hood. So I don't know where could put such a function call. The web app uses django.contrib.auth.
Additionally: How can I trigger a function when an account is activated?
You can use the signals to trigger an event when you save anything in your db, include the User from auth. You can see the django doc about this.
In your case, I think that the best way is like:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import user
#receiver(post_save, sender=User)
def user_saved(sender, instance, **kwargs):
...
In the kwargs you can see, for example, if object is created or modified. The sender is the Class and the instance is the object.

Why my Flask application can't create new user using Python Social Auth?

I am using python-social-auth package, and it has rather strange behavior, that is, I can login using existing gmail account already signup in the database, but I can't register new user.
Actually, this is a dockerize Flask application (in app.py). If I am not using supervisord, all Python Social Auth behavior works fine. That's mean, I directly call python app.py, instead of configuring it to run by supervisord in supervisord.conf.
But if I use supervisord, new user will not get registered. Application will be directed to login page.
NOTE: on the other hand, I am trying to rewrite social implementation by using Flask-OAuth.
Finally, I found the bug. These are code fix for custom pipeline for confirmed oauth process:
def pipeline_oauth_confirmed(backend, user, response, *args, **kwargs):
user.active = True
user.confirmed_at = datetime.now()
db_session.commit()
As the app need to have user.active=True before it logged in the user, the user wont' be logged in. I omitted the db_session.commit() call. Once that added, it works!

How to moderate django_openid_auth

I am implementing openid feature using django_openid_auth library which is quite amazing..
However I am looking for very specific moderation settings.
Although openid is made available for everyone..I am looking to implement the below rules
OpenId should provide email address.. Not all guys do provide
The email address should be one of the access granted list present in my db
I started to think quite possible ways like creating custom middleware, custom login_required decorator etc.. But I am unable to think a possible way of fitting them exactly in the bill.
Can anyone add suggestions would be appreciated
aquiring email address is simple enough - you just need to ask openid server for it. django-openid-auth provides settings for it:
OPENID_SREG_EXTRA_FIELDS = ['email']
In my project i also needed to do extra stuff after authentication. I solved it with signal:
def register_login_signal():
from django.contrib.auth.signals import user_logged_in
from django.contrib.auth.models import User
user_logged_in.connect(your_function_name_here, sender = User)
def your_function_name_here(sender, **kwargs):
request = kwargs.get('request')
logout(request) if request.user.email not in your_list_of_authenticated_emails else pass
and dont forget to put register_login_signal() to some place where it gets used like projects init.py file
Edit:
1st comment/question.
The extra fields part is not stated in documentation. Also, if you scan through the github package then you notice nothing like it, i'm sure. I am using older version of https://pypi.python.org/pypi/django-openid-auth/0.5. Download it, unpack and open views.py in django-openid-auth folder. Search for OPENID_SREG_EXTRA_FIELDS and you'll see it. Works like a charm if you define it in settings.py.
2nd question
Async? Nope, not really. Async would be something that gets run outside of current function stack - if you can describe it like that. This is nothing like that. Imagine it like that - in the end of login function there is check, that if there are some functions hooked on the end of login function. And they get run instantly. Its as much async as django middleware is. So not at all.
But is it right place to do it? I imagine that you have your site set up that you check if user has logged with #login_required decorator - or something like that.
So lets see how things will get executed:
1) Your openid server sends you all the information you requested with last request
2) Your django-openid-auth login_complete view takes over and authenticates user using it's backend
3) in the end of that process the signal for which you listen to is triggered, user's email is checked against your list and if the check fails, then he is instantly logged out.
4) Since the view is done, it automatically redirects you to either the url whcih was specified in original view with "next" parameter or to LOGIN_REDIRECT_URL specified in your settings
5) Before that view all the middleware and decorators get used. And if you have used something like #login_required decorator (which is smart thing to do for each login protected page), then it instantly redirects user to login page. If you bothered to add some kind of message to request in signal, then the message is displayed (given that your login/logout page supports that)
And to say something in the end - there is no stronger method than logging out.
/edit

Django switch database dynamically

I'd like to switch databases upon user login. I've created this login signal.. but it doesn't work
from django.dispatch import receiver
from django.contrib.auth.signals import user_logged_in
from django.db import connections
#receiver(user_logged_in)
def db_switch(sender, **kwargs):
user_db = 'userdb_%s' % kwargs['user'].username
cursor = connections[user_db].cursor()
The databases are defined in settings.py. Do I have to make this cursor global? Or is this all the way wrong way of doing it?
Thanks!
It's the wrong way of doing it.
Honestly I don't think there is a straightforward, stable way of doing this in Django. It's just not designed for it.
Instead, I'd set up a settings_username.py file for each user, which specifies a secondary database called personal or something. Then, after logging, have them redirect to a new domain, like username.example.com, which uses a unique .wsgi file that pulls in the settingsusername.py file.
Now, as far as the system is concerned, each website is totally separate and unique to that user. Just make sure to set the session cookie to example.com so that they're still logged in when they go to their user website.

How to get unique users across multiple Django sites powered by the "sites" framework?

I am building a Django site framework which will power several independent sites, all using the same apps but with their own templates. I plan to accomplish this by using multiple settings-files and setting a unique SITE_ID for them, like suggested in the Django docs for the django.contrib.sites framework
However, I don't want a user from site A to be able to login on site B. After inspecting the user table created by syncdb, I can see no column which might restrict a user to a specific site. I have also tried to create a user, 'bob', on one site and then using the shell command to list all users on the other side and sure enough, bob shows up there.
How can I ensure all users are restricted to their respective sites?
The most compatible way to do this would be to create a user Profile model that includes a foreign key to the Site model, then write a custom auth backend that checks the current site against the value of that FK. Some sample code:
Define your profile model, let's say in app/models.py:
from django.db import models
from django.contrib.sites.models import Site
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
site = models.ForeignKey(Site)
Write your custom auth backend, inheriting from the default one, let's say in app/auth_backend.py:
from django.contrib.auth.backends import ModelBackend
from django.contrib.sites.models import Site
class SiteBackend(ModelBackend):
def authenticate(self, **credentials):
user_or_none = super(SiteBackend, self).authenticate(**credentials)
if user_or_none and user_or_none.userprofile.site != Site.objects.get_current():
user_or_none = None
return user_or_none
def get_user(self, user_id):
try:
return User.objects.get(
pk=user_id, userprofile__site=Site.objects.get_current())
except User.DoesNotExist:
return None
This auth backend assumes all users have a profile; you'd need to make sure that your user creation/registration process always creates one.
The overridden authenticate method ensures that a user can only login on the correct site. The get_user method is called on every request to fetch the user from the database based on the stored authentication information in the user's session; our override ensures that a user can't login on site A and then use that same session cookie to gain unauthorized access to site B. (Thanks to Jan Wrobel for pointing out the need to handle the latter case.)
You can plug your own authorization and authentication backends that take the site id into consideration.
See other authentication sources on the django documentation and the authentication backends references
Besides that, if your django source is too old, you can always modify the authenticate() or login() code yourself. After all... Isn't that one of the wonders of open source. Be aware that by doing so you may affect your compatibility with other modules.
Hope this helps.
You have to know, that many people complain for Django default authorization system and privileges - it has simply rules for objects, for instances of the objects - what it means, that without writing any code it woudn't be possible.
However, there are some authorization hooks which can helps you to achieve this goal, for example:
Take a look there:
http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/models.py
and for class Permission.
You can add your own permission and define rules for them (there is a ForeignKey for User and for ContentType).
However2, without monkeypatching/change some methods it could be difficult.