Skip to content

[bug] Crash when accessing "api/v1/controller/device/[device UUID]/location/" without Superuser permssions #1110

@MichaelUray

Description

@MichaelUray

Describe the bug
If an API access to
api/v1/controller/device/[device UUID]/location/
happens without superuser permissions it casuses a crash.

Steps To Reproduce
Create an user which has limited (location) permissions, but not the Superuser status checked.
Login with that user and open the API access for the device location:
https://api.openwisp.example.com/api/v1/controller/device/83a77e46-9248-41fb-9a8f-0bf1751ef045/location/

It only reports then Server Error (500) and ends up with the following crash in the log:

nginx-1              | [c2edc3204181] - - [11/Aug/2025:01:14:15 +0200] "GET /api/v1/controller/device/83a77e46-9248-41fb-9a8f-0bf1751ef045/location/ HTTP/1.1" status: 500 145 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0" http_x_forwarded_for: 41.66.90.31 - remote_addr: 192.168.72.5 - realip_remote_addr: 192.168.72.5 - real_ip: 41.66.90.31
nginx-1              | [c2edc3204181] - - [11/Aug/2025:01:14:15 +0200] "GET /favicon.ico HTTP/1.1" status: 404 179 "https://api.openwisp.example.com/api/v1/controller/device/83a77e46-9248-41fb-9a8f-0bf1751ef045/location/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0" http_x_forwarded_for: 41.66.90.31 - remote_addr: 192.168.72.5 - realip_remote_addr: 192.168.72.5 - real_ip: 41.66.90.31
api-1                |
api-1                | [092f75d2285f] - ERROR, time: [2025-08-11 01:14:15,549],process: 14, thread: 126987757770624
api-1                | Internal Server Error: /api/v1/controller/device/83a77e46-9248-41fb-9a8f-0bf1751ef045/location/
api-1                | Traceback (most recent call last):
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/core/handlers/exception.py", line 55, in inner
api-1                |     response = get_response(request)
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/core/handlers/base.py", line 197, in _get_response
api-1                |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/views/decorators/csrf.py", line 65, in _view_wrapper
api-1                |     return view_func(request, *args, **kwargs)
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/views/generic/base.py", line 105, in view
api-1                |     return self.dispatch(request, *args, **kwargs)
api-1                |            ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/rest_framework/views.py", line 509, in dispatch
api-1                |     response = self.handle_exception(exc)
api-1                |   File "/usr/local/lib/python3.13/site-packages/rest_framework/views.py", line 469, in handle_exception
api-1                |     self.raise_uncaught_exception(exc)
api-1                |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
api-1                |     raise exc
api-1                |   File "/usr/local/lib/python3.13/site-packages/rest_framework/views.py", line 497, in dispatch
api-1                |     self.initial(request, *args, **kwargs)
api-1                |     ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/rest_framework/views.py", line 415, in initial
api-1                |     self.check_permissions(request)
api-1                |     ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/rest_framework/views.py", line 332, in check_permissions
api-1                |     if not permission.has_permission(request, self):
api-1                |            ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/openwisp_controller/mixins.py", line 19, in has_permission
api-1                |     perm = super().has_permission(request, view)
api-1                |   File "/usr/local/lib/python3.13/site-packages/openwisp_users/api/permissions.py", line 120, in has_permission
api-1                |     queryset = self._queryset(view)
api-1                |   File "/usr/local/lib/python3.13/site-packages/rest_framework/permissions.py", line 223, in _queryset
api-1                |     queryset = view.get_queryset()
api-1                |   File "/usr/local/lib/python3.13/site-packages/openwisp_controller/geo/api/views.py", line 121, in get_queryset
api-1                |     qs = super().get_queryset()
api-1                |   File "/usr/local/lib/python3.13/site-packages/openwisp_users/api/mixins.py", line 57, in get_queryset
api-1                |     qs = super().get_queryset()
api-1                |   File "/usr/local/lib/python3.13/site-packages/openwisp_users/api/mixins.py", line 105, in get_queryset
api-1                |     self.assert_parent_exists()
api-1                |     ~~~~~~~~~~~~~~~~~~~~~~~~~^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/openwisp_users/api/mixins.py", line 111, in assert_parent_exists
api-1                |     parent_queryset = self.get_organization_queryset(parent_queryset)
api-1                |   File "/usr/local/lib/python3.13/site-packages/openwisp_users/api/mixins.py", line 65, in get_organization_queryset
api-1                |     return qs.filter(self.queryset_organization_conditions)
api-1                |            ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/query.py", line 1493, in filter
api-1                |     return self._filter_or_exclude(False, args, kwargs)
api-1                |            ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/query.py", line 1511, in _filter_or_exclude
api-1                |     clone._filter_or_exclude_inplace(negate, args, kwargs)
api-1                |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/query.py", line 1518, in _filter_or_exclude_inplace
api-1                |     self._query.add_q(Q(*args, **kwargs))
api-1                |     ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/sql/query.py", line 1646, in add_q
api-1                |     clause, _ = self._add_q(q_object, can_reuse)
api-1                |                 ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/sql/query.py", line 1678, in _add_q
api-1                |     child_clause, needed_inner = self.build_filter(
api-1                |                                  ~~~~~~~~~~~~~~~~~^
api-1                |         child,
api-1                |         ^^^^^^
api-1                |     ...<7 lines>...
api-1                |         update_join_types=update_join_types,
api-1                |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
api-1                |     )
api-1                |     ^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/sql/query.py", line 1503, in build_filter
api-1                |     return self._add_q(
api-1                |            ~~~~~~~~~~~^
api-1                |         filter_expr,
api-1                |         ^^^^^^^^^^^^
api-1                |     ...<7 lines>...
api-1                |         update_join_types=update_join_types,
api-1                |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
api-1                |     )
api-1                |     ^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/sql/query.py", line 1678, in _add_q
api-1                |     child_clause, needed_inner = self.build_filter(
api-1                |                                  ~~~~~~~~~~~~~~~~~^
api-1                |         child,
api-1                |         ^^^^^^
api-1                |     ...<7 lines>...
api-1                |         update_join_types=update_join_types,
api-1                |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
api-1                |     )
api-1                |     ^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/sql/query.py", line 1526, in build_filter
api-1                |     lookups, parts, reffed_expression = self.solve_lookup_type(arg, summarize)
api-1                |                                         ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/sql/query.py", line 1333, in solve_lookup_type
api-1                |     _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
api-1                |                                 ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
api-1                |   File "/usr/local/lib/python3.13/site-packages/django/db/models/sql/query.py", line 1805, in names_to_path
api-1                |     raise FieldError(
api-1                |     ...<2 lines>...
api-1                |     )
api-1                | django.core.exceptions.FieldError: Cannot resolve keyword 'content_object' into field. Choices are: _is_deactivated, command, config, created, deviceconnection, devicefirmware, devicelocation, group, group_id, hardware_id, id, key, last_ip, mac_address, management_ip, model, modified, monitoring, name, notes, organization, organization_id, os, system, upgradeoperation, wifisession
api-1                | [092f75d2285f] - pid: 14 192.168.72.5 (-) {68 vars in 1288 bytes} [Mon Aug 11 01:14:15 2025] GET /api/v1/controller/device/83a77e46-9248-41fb-9a8f-0bf1751ef045/location/ => generated 145 bytes in 61 msecs (HTTP/1.1 500) 7 headers in 248 bytes (1 switches on core 0)
api-1                |
api-1                | [092f75d2285f] - WARNING, time: [2025-08-11 01:14:15,604],process: 14, thread: 126987530643200
api-1                | Not Found: /favicon.ico
api-1                | [092f75d2285f] - pid: 14 192.168.72.5 (-) {66 vars in 1235 bytes} [Mon Aug 11 01:14:15 2025] GET /favicon.ico => generated 179 bytes in 5 msecs (HTTP/1.1 404) 7 headers in 228 bytes (1 switches on core 1)


System Informatioon:
Docker Image: 24.11.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions