Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pytest-django: set user permissions

I work on a library for Django using pytest 3.0.6 and pytest-django 3.1.2. I have this very simple test failing, and I don't understand what happen:

# test_mytest.py
import pytest
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType


@pytest.mark.django_db
def test_user_has_perm(django_user_model):
    # Create a new user
    john_doe = django_user_model.objects.create_user('johndoe', email='[email protected]', password='123456')

    # Get or create the permission to set on user
    user_ct = ContentType.objects.get(app_label='auth', model='user')
    p, _ = Permission.objects.get_or_create(content_type=user_ct, codename='delete_user', name="Can delete user")

    # User don't have the permission
    assert john_doe.has_perm(p) is False

    # Set permission to user
    john_doe.user_permissions.add(p)
    assert john_doe.has_perm(p) is True  # ---> FAIL

Just in case, the result of the test is:

$ pytest
============================= test session starts =============================
platform win32 -- Python 3.5.3, pytest-3.0.6, py-1.4.32, pluggy-0.4.0
Django settings: testsite.settings (from ini file)
rootdir: D:\Dev\foss\django-modern-rpc, inifile: tox.ini
plugins: pythonpath-0.7.1, django-3.1.2, cov-2.4.0
collected 1 items

modernrpc\tests\test_test_test.py F

================================== FAILURES ===================================
_____________________________ test_user_has_perm ______________________________

django_user_model = <class 'django.contrib.auth.models.User'>

    @pytest.mark.django_db
    def test_user_has_perm(django_user_model):
        # Create a new user
        john_doe = django_user_model.objects.create_user('johndoe', email='[email protected]', password='123456')

        # Get or create the permission to set on user
        user_ct = ContentType.objects.get(app_label='auth', model='user')
        p, _ = Permission.objects.get_or_create(content_type=user_ct, codename='delete_user', name="Can delete user")

        # User don't have the permission
        assert john_doe.has_perm(p) is False

        # Set permission to user
        john_doe.user_permissions.add(p)
>       assert john_doe.has_perm(p) is True  # ---> FAIL
E       assert False is True
E        +  where False = <bound method PermissionsMixin.has_perm of <User: johndoe>>(<Permission: auth | user | Can delete user>)
E        +    where <bound method PermissionsMixin.has_perm of <User: johndoe>> = <User: johndoe>.has_perm

modernrpc\tests\test_test_test.py:20: AssertionError
========================== 1 failed in 0.32 seconds ===========================

The config block, from tox.ini:

[pytest]
DJANGO_SETTINGS_MODULE = testsite.settings
norecursedirs = .git __pycache__ build dist venv* .tox .vscode .cache *.egg-info
python_paths = modernrpc/tests
testpaths = modernrpc/tests
python_files = test_*.py dummy_*.py

And the DB configuration, from test settings:

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'modern_rpc.sqlite3'),
    },
}

What am I doing wrong ?

like image 360
Antwane Avatar asked Sep 12 '25 06:09

Antwane


2 Answers

You need to use a string 'app_label.codename':

Returns True if the user has the specified permission, where perm is in the format "<app label>.<permission codename>".

Also, you must clear user._perm_cache and user._user_perm_cache if you have changed permissions since the last call to has_permor retrieve a new instance of this user from the db to make sure there are no caches:

 del john_doe._perm_cache
 del john_doe._user_perm_cache 
 # OR
 john_doe = django_user_model.objects.get(username='johndoe')

This is because has_perm will call the auth backend which in turn will consult these caches first.

like image 51
user2390182 Avatar answered Sep 14 '25 18:09

user2390182


From the docs:

has_perm(perm, obj=None)

Returns True if the user has the specified permission, where perm is in the format

"<app label>.<permission codename>".

(see documentation on permissions). If the user is inactive, this method will always return False.

If obj is passed in, this method won’t check for a permission for the model, but for this specific object.

So this method accepts string not a permission object

john_doe.has_perm('auth.delete_user')

should return True. (The delete_user permission has the auth app assigned, because you have used user_ct to create it, where user_ct's app is auth).

However in your example this won't immediately happen, because there is also a permission check caching.

It will work after you refetch your object

#Be aware this only works after Django 1.9+
#https://code.djangoproject.com/ticket/26514
john_doe.refresh_from_db()
#Otherwise use:
john_doe = User.objects.get(pk=john_doe.pk)
like image 45
Todor Avatar answered Sep 14 '25 18:09

Todor