can't change user permissions during unittest in django - django

I've finally decided to make some tests for my apps but I'm stuck on testing if a user can change another user (depends on the type of the user -- I use django-rules to be able to do logical permission checks, but this is not important)
Here's the code I have so far
class RulesAndPermissionsTests(TestCase):
fixtures = ['auth_no_permissions.json', 'profiles.json', 'rules.json']
def setUp(self):
self.c = Client()
self.user = User.objects.get(username="estagiario")
self.non_staff = User.objects.get(username="fisica")
self.admin = User.objects.get(username="admin")
login = self.c.login(username='estagiario', password='estagiario')
def test_can_change_non_staff_users(self):
self.assertFalse(self.user.has_perm('logical_change_user', self.non_staff.profile)) # can't change non staff users without permission
# now add the permission and test it again
self.user.user_permissions.add(Permission.objects.get(codename='change_user'))
print self.user.get_all_permissions() # prints set([])
self.assertTrue(self.user.has_perm('logical_change_user', self.non_staff.profile))
Even after adding the permission, my user still has no permissions. Is this because I'm not allowed to create anything during the tests (is this a bad practice?)? Or does django cache the permissions somehow? If I add the permission at setUp it works, but I wanted to change it during the same test (testing with and without the permission).
Thanks in advance!

If you look at the source code for the ModelBackend, you can see that Django does cache the permissions on the user object.
You could try wiping the cache, but that could break your tests if the caching mechanism changes in future. The easiest thing to do is to refetch the user from the database in your test.
from django.contrib.auth.models import Permission
def test_can_change_non_staff_users(self):
self.assertFalse(self.user.has_perm('logical_change_user', self.non_staff.profile)) # can't change non staff users without permission
# now add the permission and test it again
self.user.user_permissions.add(Permission.objects.get(codename='change_user'))
# refetch user from the database
self.user = User.objects.get(pk=self.user.pk)
print self.user.get_all_permissions() # should now include new permission
self.assertTrue(self.user.has_perm('logical_change_user', self.non_staff.profile))

Related

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

how can I securely perform Rest requests without authentication?

This is more a process logic question than a specific language-framework one.
I am developing a mobile app and want the user to be able to use it without having to login (i.e. try it and offer a plus to the logged users), but I don´t want other persons to make post requests from let´s say Postman or any other platform than the app without having some sort of key, so what would be the approach here?
I am thinking on basic auth with some secret username:password for guests, or some kind of token, but as I am totally new on this I am not sure if it´s the correct approach, I´ve read the authentication and permissions Django Rest Framework tutorial but haven´t found a solution
I am learning Django myself and have gotten to the more advanced topics in the subject. What you could do is create a function in your permissions.py file for this. like so:
from rest_framework import permissions
class specialMobileUserPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in request.SAFE_METHODS:
return True
if request.user.id == whatever your mobile users id is:
return false
return obj.id == request.user.id # if the user is a subscribed user and they are logged in return true
return false # because we need a way out if none of the above works
So when dealing with permissions classes the permissions.SAFE_PERMISSIONS is a list of permissions that are non-destructive. So the first if statement asks are you a GET, HEAD, or other non data altering method. If so return true.
The second if statement checks the user id of the user that is making the request. And if that user id is equal to the user id you set for the mobile trail user it would return false, denying permissions to whatever this class is used on.
In your viewset you would need to add the permissions_classes variable like below
from . import permissions # your permissions.py file
class FooViewSet(viewsets.ViewSet):
permission_classes = (permissions.specialMobileUserPermissions,)
Unless you need extra functionality, that should be everything you need, all the way down to the imports. I hope I have helped.

MultipleObjectsReturned: get() returned more than one Permission -- it returned 2

My tests are failing saying get() returned 2 permissions.. But I have not assigned 2 permissions anywhere. In my views m inheriting PermissionRequiredMixin from django.contrib.auth.mixins and assigning permission. Below is my test setUp code, where error is showing:
def setUp(self):
self.material = MaterialFactory.create()
self.material_reorder = MaterialFactory.create(
opening_stock=500, reorder_qty=1000)
self.user = User.objects.create_user(
'admin', 'lennon#thebeatles.com', 'admin')
self.user.user_permissions.add(
Permission.objects.get(codename="add_material"))
It doesn't matter what you assigned. This line is causing the error
Permission.objects.get(codename="add_material")
It means that you have more than one permission with code name add_material.
To find out what other permissions are names add_material, get into Django console
python manage.py shell
once in there import auth models and check the permissions
from django.contrib.auth.models import *
for p in Permission.objects.filter(codename="add_material")
print(p.pk, p.content_type)
This should tell you what other models registered a permission with the same name.
Adding to the above solution, you can avoid loop and just use a simple code to get other permissions form same code name as:
permissions = Permission.objects.filter(codename='add_material')
print(permissions)

Django - Anonymous user group

I need to allow administrators to manage permissions for models on my site. Groups, Users, and Permissions are doing a great job of this right now. However, I also need to allow the administrators to manage the permissions of non-authenticated users - Anonymous Users. The docs say that anonymous user's group is always empty, so how can I allow administration of their permissions?
It is strange to add permissions to anonymous users. Docs say:
Django's permission framework does not have a place to store
permissions for anonymous users. However, it has a foundation that
allows custom authentication backends to specify authorization for
anonymous users. This is especially useful for the authors of
re-usable apps, who can delegate all questions of authorization to the
auth backend, rather than needing settings, for example, to control
anonymous access.
So you can set permissions to anon yuser, but with custom auth backend.
It is sometimes better to use declarative permission check, using decorators on the views with the needed permissions, like:
#permission_required('somemodel.can_add')
def add_model(request):
or leave it unrestricted for everyone(incl. anonymous user). Or some custom permission check..
Or if you want to have permissions anyway, you can always create a dummy user, let's say "AnonUser", to give it permissions, and then checking permissions to have something like:
if not user.is_authenticated():
dummy_user = User.objects.get(name="AnonUser")
if dummy_user.has_perm("somepermission"):
# bla bla bla
but this is something I'd never use..
I've gone with a custom backend to provide an anonymous group (as #Tisho suggests), which can easily be edited from the admin interface. Add the following class to AUTHENTICATION_BACKENDS in settings.py (e.g. 'appname.auth_backend.AnonymousPermissions').
Note that it's common to require a login before checking permissions (e.g. with login_required), which will have to be replaced with permission only checks.
from django.contrib.auth.models import Group
class AnonymousPermissions(object):
def get_anonymous_permissions(self):
#largely from django.contrib.auth.backends
group, group_created = Group.objects.get_or_create(name='Anonymous')
perms = group.permissions.all()
perms = perms.values_list('content_type__app_label', 'codename').order_by()
perms = set("%s.%s" % (ct, name) for ct, name in perms)
return perms
def get_group_permissions(self, user_obj, obj=None):
if user_obj.is_anonymous:
perm_cache_name = '_anonymous_perm_cache'
if not hasattr(user_obj, perm_cache_name):
setattr(user_obj, perm_cache_name, self.get_anonymous_permissions())
return getattr(user_obj, perm_cache_name)
return set()
def get_all_permissions(self, user_obj, obj=None):
return self.get_group_permissions(user_obj, obj)
def has_perm(self, user_obj, perm, obj=None):
return perm in self.get_group_permissions(user_obj, obj)

Django user get_all_permissions() is empty while user_permissions is set

I added some permissions to a user via the admin interface.
From some reason all the perm functions fail, e.g
>>> user.get_all_permissions()
set([])
But accessing the table directly, works:
>>> user.user_permissions.all()
(list of permissions as expected)
What can cause the "get_all_permissions" (and all the perm functions like has_perm()) to fail ?
Thanks
had the same problem. I am guessing that at some point you have used a self-crafted AUTHENTICATION_BACKEND? Most examples on the net of this (INCLUDING THE DJANGO 1.0 DOCUMENTATION!) don't mention that the Backends are responsible for permissions handling as well.
However, no biggie: In whatever backend file your code resides, include this import:
from django.contrib.auth.backends import ModelBackend
Then make sure the Backend you wrote extends ModelBackend, e.g.:
class EmailBackend(ModelBackend):
Should be fine.
In my case it was because of permission caching. I get the user,
added permission to user.user_permissions but user.get_all_permissions was empty set() and user.has_perm was False. This problem is only with shell not admin.
user = User.objects.get(username="User")
permission = Permission.objects.get(
codename="organizations.add_organization",
)
user.user_permissions.add(permission)
user.get_all_permissions() # set()
user.has_perm('organizations.add_organization') # False
I have to add additional line before checking permissions:
user.user_permissions.add(permission)
user = User.objects.get(username="User") # new
user.get_all_permissions()