Django admin add entry to specific groups - django

Hej!
I'm looking for a possibility to add entries by different users and groups.
In my app there are different users and different groups where one user can belong to multiple groups. The goal is that some of the groups have sensitive data so I only want members of this group to be able to read the entries but some data should be available in multiple groups.
It should also be possible to only see some information. (e.g. See the name but not the address)
For now I have the decorator #login_required for all my views and #permission_required for a persons_view. Therefore you have to be a registered user to see anything, which is great but not quite enough.
I also created groups (via the admin area) but can't filter the data for each model/view. The group either sees the data or they don't.
The registration of users is connceted to an already existing ldap system (where those groups are pre-defined. Would be great to use those!)
Is there a possibility to add entries only for a special group and view only some of the given information?
Thanks for the help!

If you want to restrict or specify a view for a group, you should use two mixins: UserPassesTestMixin and LoginRequiredMixin.
LoginRequiredMixin will just make sure the user in the request is logged in
UserPassesTestMixin will check the request from a boolean expression we define and if it returns True it will let the user in
We can set this up using a test_func method that will return either True or False
Here is an example that you can use in your view
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
class Index(LoginRequiredMixin, UserPassesTestMixin,View):
def get(self, request):
return render(request, 'nioulboy/index.html')
def test_func(self):
return self.request.user.groups.filter(name='Admin')
In the test_func method, we are returning true if the request.user has a group with the name Admin.
If it returns True, it will allow the user to view the index page if it returns False it will send back a 403 error.

for NOT class based views works this function to give permission to a whole view.
# views.py
from django.contrib.auth.decorators import login_required, user_passes_test
def user_check(user):
return user.groups.filter(name="Admin")
#user_passes_test(user_check)
#login_required
def view(request):
....

Related

Django UserPassesTestMixin confusion/questions?

I am currently working on an admin dashboard, which include specific views for company administrators only which are labeled as Business users.
The app is going to have about 10 views, and I had a few questions in regards to the UserPassesTestMixin
Basically, all of my views would include this,
def test_func(self):
return self.request.user.user_type == 'Business'
To make sure the users are Business users I am protecting the views that way.
A couple questions that I am having trouble solving on my own are:
Now with that being repeated say 10 times, is there a cleaner way to do this, rather than having
def test_func in every CBV?
The other question that comes up, if the user doesn't pass test, it redirects to the login page, which I don't really like either. These views are all returning json. If the user does not pass test, I would like to just send them to something like,
JsonResponse({'message': 'Only company administrators have access to this view'})
How would I able to change that redirect only if the user does not pass test? Keeping in mind that these views also inherit from LoginRequiredMixin as well, in which if the user is not logged in I want to keep the original redirect to the login page in tact.
Any help with this is very appreciated. This side of Django is something fairly new to me!
Now with that being repeated say 10 times, is there a cleaner way to do this, rather than having def test_func in every CBV?
Yes, you can simply make a mixin that implements the check:
from django.contrib.auth.mixins import UserPassesTestMixin
class BusinessUserMixin(LoginRequiredMixin, UserPassesTestMixin):
def test_func(self):
return self.request.user.user_type == 'Business'
def handle_no_permission(self):
return JsonResponse(
{'message': 'Only company administrators have access to this view'}
)
and then you use this mixin in your views, for example:
class MyView1(BusinessUserMixin, ListView):
# …
pass
class MyView2(BusinessUserMixin, DetailView):
# …
pass
class MyView3(BusinessUserMixin, CreateView):
# …
pass
if the user doesn't pass test, it redirects to the login page, which I don't really like either. These views are all returning json. If the user does not pass test, I would like to just send them to something like.
You can override the handle_no_permission method as well, the view will return the result of this method as result when the test fails.

Respond with Not Found if user is not authorised to view the instance

How can I modify Django behaviour, for some models only, to obscure the existence of any objects in that model but the ones the user is authorised to view?
By default, Django will respond to a query for a nonexistent object with 404 Not Found, and to an existing object that the user is not permitted to view, with 403 Forbidden.
This applies to typical Django views, and specialised views (like Django REST framwork). This is all correct, as a default.
What I'd like to do though, is to present some specific models to each user as containing only the records that user may view, and nothing else exists in there to be queried. If they're authorised to view the instance, it is shown; if it exists but they're not authorised, then Django should not reveal even whether that instance exists and should respond only with 404 Not Found.
This requirement is only for some models in the database. For example, confidential documents: the visitor should not be able to sniff around and discover which documents exist and which do not. Either they have permission to view that document, or they should not be told there's anything there just as if they queried a nonexistent document.
For other models, the normal behaviour is correct (404 if not found, 403 if it exists but the current user is not authorised).
So, a solution which overrides the exception behaviour globally is not correct for this question.
How can I convince Django to treat specific models as “unauthorised access looks like nonexistent instance”?
Django's permission system includes PermissionRequiredMixin, a mix-in class for views which will check the user has a specified permission and handle the cases where they don't.
Because PermissionRequiredMixin is a subclass of AccessMixin, perhaps a new specific mix-in class can be made that overrides AccessMixin.handle_no_permission:
from django.contrib.auth.mixins import PermissionRequiredMixin
class PermissionRequiredOrDoesNotExistMixin(PermissionRequiredMixin):
""" A mix-in class that handles “not permitted” as though it were “not found”. """
def handle_no_permission(self):
...
The third-party library django-guardian implements a customised PermissionRequiredMixin for views (this class is subclassed from django.contrib.auth.mixins.PermissionRequiredMixin).
That custom class has return_403 and return_404 attributes, to specify the view should return one of those HTTP responses (instead of the default behaviour to redirect to a login page) when the user does not have the required permission.
Yes, or:
from django.views import View
from django.contrib.auth.mixins import UserPassesTestMixin
from django.http import Http404
class StaffRequired(UserPassesTestMixin):
def test_func(self):
return self.request.user.is_staff
def handle_no_permission(self):
raise Http404
class HiddenView(StaffRequired, View):
pass

Can Django set permissions on apps instead of models

I want to have authority control over my project. Some users can see some app's entries while others can't. I want to set permissions on apps instead of models, I have searched, but only found how to set permissions on models. So, I want to know how to set permissions on apps.
You can make decorators to selectively allow users to access the page
Make this decorator
def filter_users(func):
def checker(request,*args,**kwargs):
if some_condition: #This condition will tell you whether to allow this perticular user
return func(request,*args,**kwargs)
else:
return render('invalid.html') #return a page telling the user that he is not allowed
return checker
Now just apply this decorator to all the views that you want to prevent 'some' users from accessing.
Ex:
#filter_users
def some_view(request):
#Do Something...
Now only allowed users will be able to see the view, rest all will get the invalid page
You can apply this decorator to all the views of the perticular app that you want to restrict access to

Create a User Profile or other Django Object automatically

I have setup a basic Django site and have added login to the site. Additionally, I have created a Student (Profile) model that expands upon the built in User one. It has a OneToOne relationship with the User Model.
However, I have yet not come right with forcing the user to automatically create a Profile the first time they log in. How would I make sure they are not able to progress through anything without creating this?
I have tried by defining the following in views:
def UserCheck(request):
current_user = request.user
# Check for or create a Student; set default account type here
try:
profile = Student.objects.get(user = request.user)
if profile == None:
return redirect('/student/profile/update')
return True
except:
return redirect('/student/profile/update')
And thereafter adding the following:
UserCheck(request)
at the top of each of my views. This however, never seems to redirect a user to create a profile.
Is there a best way to ensure that the User is forced to create a profile object above?
Looks like you're attempting to do something similar to Django's user_passes_test decorator (documentation). You can turn the function you have into this:
# Side note: Classes are CamelCase, not functions
def user_check(user):
# Simpler way of seeing if the profile exists
profile_exists = Student.objects.filter(user=user).exists()
if profile_exists:
# The user can continue
return True
else:
# If they don't, they need to be sent elsewhere
return False
Then, you can add a decorator to your views:
from django.contrib.auth.decorators import user_passes_test
# Login URL is where they will be sent if user_check returns False
#user_passes_test(user_check, login_url='/student/profile/update')
def some_view(request):
# Do stuff here
pass

Django privilege to template

I have a separate control panel which will manage some user information and other data .Also in Django Group i have 3 groups such as 1) Owners ,2)Developers , 3 )Clients .
I have to give an access privilege of this control-panel template to only Group owner and Developers .
Template Name : control_panel.html
View : control-panel(request) : yet to implement the code for this .
Model : accessing 3 tables with product information and its prices
If I were you, I would not tie access to groups, but rather to permissions, this way it is much more granular, and you can later allow other groups access to this view, by adding a permission to that group. In this case all you have to do is decorate your view like this:
from django.contrib.auth.decorators import permission_required
#permission_required('some_permission')
def control_panel(request):
# here goes your view
But if you prefer to use groups, you can do it like this:
from django.contrib.auth.decorators import user_passes_test
#user_passes_test(labmda u: 'Owners' in u.groups.values_list('name', flat=True))
def control_panel(request):
# here goes your view
I implemented this way . this is more efficient method to do
#permission_required('admin.some_permission',login_url="/admin/permission_required_error")
def control_panel(request):#your code here
If the user dont have the permission then it will redirect to the login_url location . Here we can Show the Description about this permission .
In the login_url View :
def permission_required_error(request):
context={}
next_page = request.GET.get('next', '')
context={'next_page':next_page}
return render_to_response ('permission_required_error.html', context, context_instance=RequestContext(request))