Django Authorization- Identify the User in Views - django

I'd like to develop an app for multiple clients that displays analytics data specific to their account. So far I've set up my views file using the #login_required decorator for restricted pages. Now I need to identify that user to obtain their data.
A bit of research suggests that I could use:
from django.contrib.sessions.models import Session
from django.contrib.auth.models import User
session_key = request.session.session_key()
session = Session.objects.get(session_key=session_key)
uid = session.get_decoded().get('_auth_user_id')
Which obviously returns the user_id, which I can use as a field in the analytics database to identify which user the data belongs to.
Is this the best way to go about doing this, or can anyone suggest a better/more elegant alternative?
Note: I haven't tested the solution I've proposed, it just seems workable in my head.

You can access the logged in user on the request object.
#login_required
def my_view(request):
user = request.user
do_something(user)
...

when you are in a view, you can access the currently logged in user (object) by
def myview(request):
request.user
# or, if you want,
request.user.id

Related

How to make a django user inactive and invalidate all their sessions

To make a user inactive I usually do:
User.objects.get(pk=X).update(is_active=False)
However, this doesn't log out the user or do anything session-related. Is there a built-in in django to make a user immediately inactive, or what would be the best way to accomplish this?
One similar answer is this: https://stackoverflow.com/a/954318/651174, but that doesn't work great if there are millions of sessions (it's a brute force way iterating over all sessions). Though this is from 2009, so hopefully there's a better way as of today.
As mentioned, you can use django-user-session or django-qsessions. But these include some other metadata such as user agent and ip address, and you may not want these for some reason. Then you need to write your custom session backend
I adjusted the example a bit and created one to the my needs.
session_backend.py:
from django.contrib.auth import get_user_model
from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.contrib.sessions.base_session import AbstractBaseSession
from django.db import models
User = get_user_model()
class QuickSession(AbstractBaseSession):
# Custom session model which stores user foreignkey to asssociate sessions with particular users.
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
#classmethod
def get_session_store_class(cls):
return SessionStore
class SessionStore(DBStore):
#classmethod
def get_model_class(cls):
return QuickSession
def create_model_instance(self, data):
obj = super().create_model_instance(data)
try:
user_id = int(data.get('_auth_user_id'))
user = User.objects.get(pk=user_id)
except (ValueError, TypeError, User.DoesNotExist):
user = None
obj.user = user
return obj
and in settings.py:
SESSION_ENGINE = 'path.to.session_backend'
To delete all session for a user:
from session_backend import QuickSession
QuickSession.objects.filter(user=request.user).delete()
You may write your custom save method for user model to automatically delete all sessions
for the user if the is_active field is set to False.
Keep in mind that, user field for those who are not logged in will be NULL.
Changing the user's password invalidates all the user's sessions since around Django version 2.2. (This works without scanning the whole session table. An HMAC of the password field is saved on login, and on any request where the request.user is accessed, the login session is treated as no-longer-valid if the current HMAC does not match.)
https://docs.djangoproject.com/en/2.2/topics/auth/default/#session-invalidation-on-password-change-1
user = User.objects.get(pk=user_id)
user.is_active = False
user.set_unusable_password()
user.save()

Controlling Django auth user access to specific object instances

In my Django project, I have various users created by Django's built-in authentication system. Each user can create their own instances of the App model. I would like to restrict user access to objects such that users can only view the instances they have created. To do that I have created this view:
#login_required
def appDetail(request, app_id):
try:
app = App.objects.get(pk=app_id)
# Testing if the currently logged in user is
# the same as the user that created the 'app':
if request.user.id == app.user.user.id:
if request.method == 'POST':
form = AppForm(request.POST, instance=app)
if form.is_valid():
edited_app = form.save()
return HttpResponseRedirect('/thanks/')
else:
form = AppForm(instance=app)
# If 'app' does not belong to logged in user, redirect to 'accessdenied' page:
else:
return HttpResponseRedirect('/accessdenied/')
except LeaveApp.DoesNotExist:
raise Http404
return render(request, 'AppDetail.html', {'form':form})
It works, but I'm wondering if there's a more commonly accepted and/or safe way to do this?
This is called row-level permissions and it's a very common problem. See here for all the apps that solve it.
If that particular test is all you need to do, go for a custom solution like yours (though, since it's boilerplate, it's preferable to move it to a decorator). Otherwise, just use an existing app.
I would put the form submission in a different view and write a custom decorator, which you could also use for similar issues.
I would also return a 404 instead of access denied. You might not want to show users that you are protecting something.
There is a decorator called user_passes_test that restricts access to a view based on if the user passes a certain check
from django.contrib.auth.decorators import login_required, user_passes_test
#login_required
#user_passes_test(lambda user: user.username == app.user.user.id)
MyView(request):
...
You can also add in an optional argument for a url to redirect to in the event they fail the check.
Trying to do this from the admin page is also pretty easy, but takes a few extra steps.
Docs Here

Creating user profile pages in Django

I'm a beginner in Django. I need to setup a website, where each user has a profile page. I've seen django admin. The profile page for users, should store some information which can be edited by the user only. Can anyone point me out how that is possible?. Any tutorial links would be really helpful. Also, are there any modules for django, which can be used for setting up user page.
You would just need to create a view that's available to an authenticated user and return a profile editing form if they're creating a GET request or update the user's profile data if they're creating a POST request.
Most of the work is already done for you because there are generic views for editing models, such as the UpdateView. What you need to expand that with is checking for authenticated users and providing it with the object that you want to provide editing for. That's the view component in the MTV triad that provides the behavior for editing a user's profile--the Profile model will define the user profile and the template will provide the presentation discretely.
So here's some behavior to throw at you as a simple solution:
from django.contrib.auth.decorators import login_required
from django.views.generic.detail import SingleObjectMixin
from django.views.generic import UpdateView
from django.utils.decorators import method_decorator
from myapp.models import Profile
class ProfileObjectMixin(SingleObjectMixin):
"""
Provides views with the current user's profile.
"""
model = Profile
def get_object(self):
"""Return's the current users profile."""
try:
return self.request.user.get_profile()
except Profile.DoesNotExist:
raise NotImplemented(
"What if the user doesn't have an associated profile?")
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
"""Ensures that only authenticated users can access the view."""
klass = ProfileObjectMixin
return super(klass, self).dispatch(request, *args, **kwargs)
class ProfileUpdateView(ProfileObjectMixin, UpdateView):
"""
A view that displays a form for editing a user's profile.
Uses a form dynamically created for the `Profile` model and
the default model's update template.
"""
pass # That's All Folks!
You can
create another Model for storing profile information about user
add AUTH_PROFILE_MODULE='yourprofileapp.ProfileModel' to settings.py
In profile editing view, allow only logged in users to edit their own profiles
example:
#login_required
def edit_profile(request):
'''
edit profile of logged in user i.e request.user
'''
You can also make sure that whenever new user is created the user's profile is also created using django's signals
Read about storing additional information about users from django documentation

Django - what's the practice and most secure method of deleting or editing records for logged in users?

I have some sections on my web site where only logged in users can see their resources.
I also want to make absolutely sure that only that authorized user can modify and delete his/her records. What's the best practice and more secure way of accomplishing this in Django?
Real examples would be truly appreciated.
For my project, I created a Decorator that checked if the right user was logged in:
#decorator.py
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
def same_user_required(func):
def wrapper(request, user):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('login-view'))
if not user == request.user.username:
return HttpResponseRedirect(reverse('login-view'))
return func(request, user)
return wrapper
You then add it to any views that need checking:
#view_profile.py
from apps.utilities.decorators import same_user_required
#same_user_required
def edit_profile(request, user):
Note that my URL contains the username /profile/edit/<username>, which is where the parameter comes from, in the edit_profile view.
Another way is to use the Django built-in decorator, user_passes_test (see Django Book Chap 14 for an example of its usage. You then just have to write the test, not the decorator boilerplate code.

Profile page getting acess to user object in Django

I have a requirement where I have to register users first via email. So, I went with django-registraton and I managed to integrate tat module into my django project.
After a successful login, the page redirects to 'registration/profile.html'.
I need to get access to the user object which was used in the authentication.
I need this object to make changes to a model which holds custom profile information about my users. I have already defined this in my models.py
Here is the URL I am using to re-direct to my template..
url(r'^profile/$',direct_to_template,{'template':'registration/profile.html'}),
So my question is this... after login, the user has to be taken to a profile page that needs to be filled up.
Any thoughts on how I can achieve this?
I have set up something similar earlier. In my case I defined new users via the admin interface but the basic problem was the same. I needed to show certain page (ie. user settings) on first log in.
I ended up adding a flag (first_log_in, BooleanField) in the UserProfile model. I set up a check for it at the view function of my frontpage that handles the routing. Here's the crude idea.
views.py:
def get_user_profile(request):
# this creates user profile and attaches it to an user
# if one is not found already
try:
user_profile = request.user.get_profile()
except:
user_profile = UserProfile(user=request.user)
user_profile.save()
return user_profile
# route from your urls.py to this view function! rename if needed
def frontpage(request):
# just some auth stuff. it's probably nicer to handle this elsewhere
# (use decorator or some other solution :) )
if not request.user.is_authenticated():
return HttpResponseRedirect('/login/')
user_profile = get_user_profile(request)
if user_profile.first_log_in:
user_profile.first_log_in = False
user_profile.save()
return HttpResponseRedirect('/profile/')
return HttpResponseRedirect('/frontpage'')
models.py:
from django.db import models
class UserProfile(models.Model):
first_log_in = models.BooleanField(default=True, editable=False)
... # add the rest of your user settings here
It is important that you set AUTH_PROFILE_MODULE at your setting.py to point to the model. Ie.
AUTH_PROFILE_MODULE = 'your_app.UserProfile'
should work.
Take a look at this article for further reference about UserProfile. I hope that helps. :)