Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ and this project attempts to adhere to [Semantic Versioning](https://semver.org/

## [Unreleased]

### Changed

- Check for the existence of a user attached to the `request` object passed in to `django_simple_nav.permissions.check_item_permissions` has been moved to allow for an early return if there is no user. There are instances where the `django.contrib.auth` app can be installed, but no user is attached to the request object. This change will allow this function to correctly be used in those instances.

## [0.6.0]

### Added
Expand Down
14 changes: 4 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,9 @@ tag = false
version_pattern = "MAJOR.MINOR.PATCH[PYTAGNUM]"

[tool.bumpver.file_patterns]
".copier/package.yml" = [
'current_version: {version}'
]
"src/django_simple_nav/__init__.py" = [
'__version__ = "{version}"'
]
"tests/test_version.py" = [
'assert __version__ == "{version}"'
]
".copier/package.yml" = ['current_version: {version}']
"src/django_simple_nav/__init__.py" = ['__version__ = "{version}"']
"tests/test_version.py" = ['assert __version__ == "{version}"']

[tool.coverage.paths]
source = ["src"]
Expand Down Expand Up @@ -142,7 +136,7 @@ ignore_missing_model_attributes = true
[tool.pytest.ini_options]
addopts = "--create-db -n auto --dist loadfile --doctest-modules"
django_find_project = false
norecursedirs = ".* bin build dist *.egg htmlcov logs node_modules templates venv"
norecursedirs = ".* bin build dist *.egg example htmlcov logs node_modules templates venv"
python_files = "tests.py test_*.py *_tests.py"
pythonpath = "src"
testpaths = ["tests"]
Expand Down
28 changes: 12 additions & 16 deletions src/django_simple_nav/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import logging
from typing import TYPE_CHECKING
from typing import Protocol
from typing import cast

from django.apps import apps
from django.contrib.auth.models import AbstractUser
from django.http import HttpRequest

if TYPE_CHECKING:
Expand All @@ -15,29 +15,25 @@
logger = logging.getLogger(__name__)


class User(Protocol):
is_authenticated: bool
is_staff: bool
is_superuser: bool

def has_perm(self, perm: str) -> bool: ... # pragma: no cover


def check_item_permissions(item: NavGroup | NavItem, request: HttpRequest) -> bool:
if not apps.is_installed("django.contrib.auth"):
logger.warning(
"The 'django.contrib.auth' app is not installed, so no permissions will be checked."
)
return True

user = cast(User, request.user)
if not hasattr(request, "user"):
# if no user attached to request, we assume that the user is not authenticated
# and we should hide if *any* permissions are set
return not item.permissions

for idx, perm in enumerate(item.permissions):
if not hasattr(request, "user"):
return False

user = cast(User, request.user)
# explicitly cast to AbstractUser to make static type checkers happy
# `django-stubs` types `request.user` as `django.contrib.auth.base_user.AbstractBaseUser`
# as opposed to `django.contrib.auth.models.AbstractUser` or `django.contrib.auth.models.User`
# so any type checkers will complain if this is not casted
user = cast(AbstractUser, request.user)

for idx, perm in enumerate(item.permissions):
user_perm = user_has_perm(user, perm)

if not user_perm:
Expand All @@ -58,7 +54,7 @@ def check_item_permissions(item: NavGroup | NavItem, request: HttpRequest) -> bo
return True


def user_has_perm(user: User, perm: str) -> bool:
def user_has_perm(user: AbstractUser, perm: str) -> bool:
"""Check if the user has a certain auth attribute or permission."""

has_perm = False
Expand Down