The requirement is to display the logged in username and his role in the organization in all the pages at the top right corner.
The role can be identified by the permissions he is given.
My approach:
I thought of creating a user-defined function in views.py and call it in each and every other function. The function that I create should check the current user's permissions and based on them his role should be decided. All these details must be sent to base.html so that it will render and display the username and his role every time the page is loaded.
Any suggestions on other approaches are welcomed and if my approach is entirely wrong please let me know.
In the views you could do something like:
context = {
'username': username,
'role': role,
}
return render('template.html', context)
and in template.html you would then render it like:
{username} {role}
You can find your answer in the comments section of: Can I call a view from within another view?
btw, yes you can create a method to validate user/department. below is the sample of how I have done in my app.
A better approach I feel is to save such details in DB as a boolean flag and use django admin's capabilities to ease your work.
def validate_details(request):
user = request.GET['username']
user_details = User.objects.filter(name=user).values_list("name", "department", flat=True)
# Call validation method defined in view
is_ok = validate_user(user_details)
if is_ok:
return render_to_response("main/landing.html", {"user": user_details["name"],"department":user_details["department"]}, context_instance=RequestContext(request))
else:
return render_to_response("main/landing.html", {"user":"NA","department":"NA"}, context_instance=RequestContext(request))
Related
In my context processor.py file I filter a table according to a user. This all works fine :
def subject_renderer(request):
return {"Calicount": Tools_Calibrated.objects.filter(
user=request.user, calibrated=True, issued=True).count(),
}
The problem comes in when I log out and try log back in. Because there is no authenticated user at login I cannot call the query in the context processor.
Question: Why is my context processor function being called when I am not calling the key in the login view template. I know how to solve this with templates but I was wondering how to solve it with context processors. Essentially omit it from one view.
Thank you in advance
It is possible that the user is not logged in, so in that case you can not pass Calicount.
You thus should implement your context processor with:
def subject_renderer(request):
if request.user.is_authenticated:
return {
'Calicount': Tools_Calibrated.objects.filter(user=request.user, calibrated=True, issued=True).count()
}
return {}
I'm new to the web development world, to Django, and to applications that require securing the URL from users that change the foo/bar/pk to access other user data.
Is there a way to prevent this? Or is there a built-in way to prevent this from happening in Django?
E.g.:
foo/bar/22 can be changed to foo/bar/14 and exposes past users data.
I have read the answers to several questions about this topic and I have had little luck in an answer that can clearly and coherently explain this and the approach to prevent this. I don't know a ton about this so I don't know how to word this question to investigate it properly. Please explain this to me like I'm 5.
There are a few ways you can achieve this:
If you have the concept of login, just restrict the URL to:
/foo/bar/
and in the code, user=request.user and display data only for the logged in user.
Another way would be:
/foo/bar/{{request.user.id}}/
and in the view:
def myview(request, id):
if id != request.user.id:
HttpResponseForbidden('You cannot view what is not yours') #Or however you want to handle this
You could even write a middleware that would redirect the user to their page /foo/bar/userid - or to the login page if not logged in.
I'd recommend using django-guardian if you'd like to control per-object access. Here's how it would look after configuring the settings and installing it (this is from django-guardian's docs):
>>> from django.contrib.auth.models import User
>>> boss = User.objects.create(username='Big Boss')
>>> joe = User.objects.create(username='joe')
>>> task = Task.objects.create(summary='Some job', content='', reported_by=boss)
>>> joe.has_perm('view_task', task)
False
If you'd prefer not to use an external library, there's also ways to do it in Django's views.
Here's how that might look:
from django.http import HttpResponseForbidden
from .models import Bar
def view_bar(request, pk):
bar = Bar.objects.get(pk=pk)
if not bar.user == request.user:
return HttpResponseForbidden("You can't view this Bar.")
# The rest of the view goes here...
Just check that the object retrieved by the primary key belongs to the requesting user. In the view this would be
if some_object.user == request.user:
...
This requires that the model representing the object has a reference to the User model.
In my project, for several models/tables, a user should only be able to see data that he/she entered, and not data that other users entered. For these models/tables, there is a user column.
In the list view, that is easy enough to implement, just filter the query set passed to the list view for model.user = loggged_id.user.
But for the detail/update/delete views, seeing the PK up there in the URL, it is conceivable that user could edit the PK in the URL and access another user's row/data.
I'm using Django's built in class based views.
The views with PK in the URL already have the LoginRequiredMixin, but that does not stop a user from changing the PK in the URL.
My solution: "Does Logged In User Own This Row Mixin"
(DoesLoggedInUserOwnThisRowMixin) -- override the get_object method and test there.
from django.core.exceptions import PermissionDenied
class DoesLoggedInUserOwnThisRowMixin(object):
def get_object(self):
'''only allow owner (or superuser) to access the table row'''
obj = super(DoesLoggedInUserOwnThisRowMixin, self).get_object()
if self.request.user.is_superuser:
pass
elif obj.iUser != self.request.user:
raise PermissionDenied(
"Permission Denied -- that's not your record!")
return obj
Voila!
Just put the mixin on the view class definition line after LoginRequiredMixin, and with a 403.html template that outputs the message, you are good to go.
In django, the currently logged in user is available in your views as the property user of the request object.
The idea is to filter your models by the logged in user first, and then if there are any results only show those results.
If the user is trying to access an object that doesn't belong to them, don't show the object.
One way to take care of all of that is to use the get_object_or_404 shortcut function, which will raise a 404 error if an object that matches the given parameters is not found.
Using this, we can just pass the primary key and the current logged in user to this method, if it returns an object, that means the primary key belongs to this user, otherwise it will return a 404 as if the page doesn't exist.
Its quite simple to plug it into your view:
from django.shortcuts import get_object_or_404, render
from .models import YourModel
def some_view(request, pk=None):
obj = get_object_or_404(YourModel, pk=pk, user=request.user)
return render(request, 'details.html', {'object': obj})
Now, if the user tries to access a link with a pk that doesn't belong to them, a 404 is raised.
You're going to want to look into user authentication and authorization, which are both supplied by [Django's Auth package] (https://docs.djangoproject.com/en/4.0/topics/auth/) . There's a big difference between the two things, as well.
Authentication is making sure someone is who they say they are. Think, logging in. You get someone to entire their user name and password to prove they are the owner of the account.
Authorization is making sure that someone is able to access what they are trying to access. So, a normal user for instance, won't be able to just switch PK's.
Authorization is well documented in the link I provided above. I'd start there and run through some of the sample code. Hopefully that answers your question. If not, hopefully it provides you with enough information to come back and ask a more specific question.
This is a recurring question and also implies a serious security flaw. My contribution is this:
There are 2 basic aspects to take care of.
The first is the view:
a) Take care to add a decorator to the function-based view (such as #login_required) or a mixin to the class-based function (such as LoginRequiredMixin). I find the official Django documentation quite helpful on this (https://docs.djangoproject.com/en/4.0/topics/auth/default/).
b) When, in your view, you define the data to be retrieved or inserted (GET or POST methods), the data of the user must be filtered by the ID of that user. Something like this:
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=User.objects.filter(pk=self.request.user.id))
return super().get(request, *args, **kwargs)
The second aspect is the URL:
In the URL you should also limit the URL to the pk that was defined in the view. Something like this:
path('int:pk/blog-add/', AddBlogView.as_view(), name='blog-add'),
In my experience, this prevents that an user sees the data of another user, simply by changing a number in the URL.
Hope it helps.
In django CBV (class based views) you can prevent this by comparing the
user entered pk and the current logged in user:
Note: I tested it in django 4 and python 3.9.
from django.http import HttpResponseForbidden
class UserDetailView(LoginRequiredMixin, DetailView):
model = your_model
def dispatch(self, request, *args, **kwargs):
if kwargs.get('pk') != self.request.user.pk:
return HttpResponseForbidden(_('You do not have permission to view this page'))
return super().dispatch(request, *args, **kwargs)
I'm brand new to django and fairly new to programming in general. I've done the django tutorial and searched the web for an answer to this question, but to no avail, so now I'm here. I am confused how post works with django. All of the tutorials I've looked at how have a return function in views that displays the webpage. I get that. But then how does a user update data if the page is being rendered from that return statement? After the return there can't be any more updates because the function stops, right? What am I missing here? Any help would be greatly appreciated, I'm getting fairly desperate here.
One pattern for Django views (by no means the only pattern) is to check the request method (GET or POST) at the beginning of the view. If it is POST, then handle the incoming data (before the view returns), and then return either a rendered template, or a redirect.
def view_function(request):
if request.method == 'POST':
if data_is_valid(request.POST):
save_data(request.POST)
return HttpResponseRedirect('/somewhere/good')
else:
return render('template', {'errors': what_went_wrong}
else:
return render('template')
The user updates data in the logic of the view function. That is to say, if the user wishes to update something, you place the update logic in the view function before the return. For example, you would do this:
def update(request):
item = <some model>.objects.get(<something>)
<more code>
return <something>
Usually an edit view function contains two parts -- one for updating data, and the other for displaying the update form. For example,
def user_edit(request):
if request.method == 'POST': # is this a save action?
# save the user data
user_id = request.POST.get('user_id')
username = request.POST.get('username')
description = request.POST.get('description')
user = User.objects.get(id=user_id)
user.username = username
user.description = description
user.save()
return HttpResponseRedirect('/user/') # redirect to index
else:
# show the edit form
user_id = request.GET.get('user_id')
user = User.object.get(id=user_id)
return render_to_response('/user/edit.html', { 'user': user })
There are many different choices for the if request.method == 'POST' line. You can also use if request.POST.get('user_id') to check if specified field is set, to determine if this is a save action.
I have several function that need to have a 'redirect' filter. The redirect filter goes something like this --
1) if a user is not logged in and has no session data, redirect to login page.
2) if a user is logged in and has already filled out the page, redirect to user home.
3) if a user is logged in and has not already filled out the page, stay on the page.
4) if a user is not logged in and has session data, stay on the page
I've started to convert the functions into a class-based approach to make it more efficient and less code (previously my view functions were pretty massive). This is my first stab at trying make something class-based, and this is what I have so far --
def redirect_filter(request):
if request.user.is_authenticated():
user = User.objects.get(email=request.user.username)
if user.get_profile().getting_started_boolean:
return redirect('/home/') ## redirect to home if a logged-in user with profile filled out
else:
pass ## otherwise, stay on the current page
else
username = request.session.get('username')
if not username: ## if not logged in, no session info, redirect to user login
return redirect('/account/login')
else:
pass ## otherwise, stay on the current page
def getting_started_info(request, positions=[]):
location = request.session.get('location')
redirect_filter(request)
if request.method == 'POST':
form = GettingStartedForm(request.POST)
...(run the function)...
else:
form = GettingStartedForm() # inital = {'location': location}
return render_to_response('registration/getting_started_info1.html', {'form':form, 'positions': positions,}, context_instance=RequestContext(request))
Obviously, this view is not fully working yet. How would I convert this into something that's functional?
Also, I have three variables that will need to be reused in several of the getting_started functions:
user = User.objects.get(email=request.user.username)
profile = UserProfile.objects.get(user=user)
location = profile.location
Where would I put these variable definitions so I can reuse them in all the functions, and how would I call them?
Thank you.
Django actually already includes a login_required decorator that makes handling user authentication trivial. Just include the following at the top of your view.py page:
from django.contrib.auth.decorators import login_required
and then add
#login_required
before any views that require a login. It even handles redirecting the user to the appropriate page once they log in.
More info here:
https://docs.djangoproject.com/en/dev/topics/auth/#the-login-required-decorator
This should greatly simplify your views, and may result in not having to write a separate class, since all that's left is a simple re-direct.
As for the variables, each request already contains a request.user object with information on the user. You can do a search in the docs for Request and response objects to learn more.
You can use that user object to get the profile variable by extending the user module. Set AUTH_PROFILE_MODULE = 'myapp.UserProfile' in your Settings, which will allow you to access a users profile as follows:
user.get_profile().location.
More about that here:
http://www.b-list.org/weblog/2006/jun/06/django-tips-extending-user-model/
I use the backend solution from django. I just want to get a username from the cookie or the session_key to get to know the user. How I can do it?
from django.contrib.auth.models import User
from django.contrib.sessions.models import Session
def start(request, template_name="registration/my_account.html"):
user_id = request.session.get('session_key')
if user_id:
name = request.user.username
return render_to_response(template_name, locals())
else:
return render_to_response('account/noauth.html')
Only else is coming up. What am I doing wrong?
Am I right then that authenticated means he is logged in?
--> Okay this I got!
Firstly, if you have some clarification to a question, update the question, don't post an answer or (even worse) another question, as you have done. Secondly, if the user is logged out, by definition he doesn't have a username.
I mean the advantage of Cookies is to identify a user again. I just want to place his name on the webpage. Even if he is logged out. Or isnt't it possible?
You can check if a user is authenticated by calling the, apptly named, is_authenticated method. Your code would then look somewhat like this:
def start(request, template_name="registration/my_account.html"):
if request.user.is_authenticated():
name = request.user.username
return render_to_response(template_name, locals())
else:
return render_to_response('account/noauth.html')
No need to access the session yourself, Django handles all of that automatically (provided you use both django.contrib.sessions and django.contrib.auth).
/edit: in order to have a user's username, he needs to be authenticated. There's no good way around that.
piquadrat has absolutely the right answer, but if for some reason you do need to get the user from the session, you call get_decoded() on the session object:
session_data = request.session.get_decoded()
user_id = session_data['_auth_user_id']
You need to enable the AuthenticationMiddleware and SessionMiddleware in your MIDDLEWARE_CLASSES setting in your settings.py to access request.user in your views.
http://docs.djangoproject.com/en/1.2/topics/auth/#authentication-in-web-requests
You can then access the username using request.user.username