netbox-community / netbox-topology-views

A netbox plugin that draws topology views
Apache License 2.0
764 stars 65 forks source link

Fails to work with LOGIN_REQUIRED=False #575

Open candlerb opened 1 month ago

candlerb commented 1 month ago

NetBox version

v4.1.3

Topology Views version

v4.1.0

Steps to Reproduce

Install NTV on a server configured for anonymous access:

LOGIN_REQUIRED = False

EXEMPT_VIEW_PERMISSIONS = [
    '*',
]

Click on the "Topology Views > Topology" nav item (/plugins/netbox_topology_views/topology/)

Expected Behavior

Topology view page should be displayed normally

Observed Behavior

An exception is raised:

Server Error
There was a problem with your request. Please contact an administrator.

The complete exception is provided below:

<class 'ValueError'>

Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x73edd8f902f0>>": "ObjectChange.user" must be a "User" instance.

Python version: 3.12.3
NetBox version: 4.1.3
Plugins: 
  netbox_prometheus_sd: 0.6
  netbox_topology_views: 4.1.0

Setting DEBUG=True, the backtrace suggests it's to do with a save hook from IndividualOptions.objects.get_or_create()

Environment:

Request Method: GET
Request URL: http://noc.ws.nsrc.org/netbox/plugins/netbox_topology_views/topology/

Django Version: 5.0.9
Python Version: 3.12.3
Installed Applications:
['django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.humanize',
 'django.forms',
 'corsheaders',
 'debug_toolbar',
 'django_filters',
 'django_htmx',
 'django_tables2',
 'django_prometheus',
 'strawberry_django',
 'mptt',
 'rest_framework',
 'social_django',
 'taggit',
 'timezone_field',
 'core',
 'account',
 'circuits',
 'dcim',
 'ipam',
 'extras',
 'tenancy',
 'users',
 'utilities',
 'virtualization',
 'vpn',
 'wireless',
 'django_rq',
 'drf_spectacular',
 'drf_spectacular_sidecar',
 'netbox_prometheus_sd.PrometheusSD',
 'netbox_topology_views.TopologyViewsConfig']
Installed Middleware:
['strawberry_django.middlewares.debug_toolbar.DebugToolbarMiddleware',
 'corsheaders.middleware.CorsMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'django_htmx.middleware.HtmxMiddleware',
 'netbox.middleware.RemoteUserMiddleware',
 'netbox.middleware.CoreMiddleware',
 'netbox.middleware.MaintenanceModeMiddleware']

Traceback (most recent call last):
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/db/models/query.py", line 948, in get_or_create
    return self.get(**kwargs), False
           ^^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/db/models/query.py", line 649, in get
    raise self.model.DoesNotExist(
    ^

During handling of the above exception (IndividualOptions matching query does not exist.), another exception occurred:
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/views/generic/base.py", line 104, in view
    return self.dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/contrib/auth/mixins.py", line 109, in dispatch
    return super().dispatch(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/views/generic/base.py", line 143, in dispatch
    return handler(request, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/netbox_topology_views/views.py", line 726, in get
    individualOptions, created = IndividualOptions.objects.get_or_create(

  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/db/models/query.py", line 955, in get_or_create
    return self.create(**params), True
           ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/db/models/query.py", line 679, in create
    obj.save(force_insert=True, using=self.db)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/db/models/base.py", line 822, in save
    self.save_base(
    ^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/db/models/base.py", line 924, in save_base
    post_save.send(
    ^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/dispatch/dispatcher.py", line 189, in send
    response = receiver(signal=self, sender=sender, **named)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/netbox/netbox/core/signals.py", line 92, in handle_changed_object
    objectchange.user = request.user
    ^^^^^^^^^^^^^^^^^
  File "/opt/netbox-4.1.3/venv/lib/python3.12/site-packages/django/db/models/fields/related_descriptors.py", line 284, in __set__
    raise ValueError(
    ^

Exception Type: ValueError at /netbox/plugins/netbox_topology_views/topology/
Exception Value: Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x763d43988ce0>>": "ObjectChange.user" must be a "User" instance.
dreng commented 1 month ago

Hi Brian,

thank you for reporting this issue. I didn't even know that a non-login-mode exists. Individual options - as the name implies - store options for registered individuals (aka users), which obviously are not anonymous. NTV tries to load/save options for a specific user id, which does not apply here.

I'm not sure if individual options make sense at all for users who share these options with other anonymous users. How do you suggest we should handle this issue?

candlerb commented 1 month ago

I guess the simplest would be to hide the Topology menu for non-authenticated users. Alternatively, have a fixed set of defaults that anonymous users get, and can't save. (Like the settings that used to be in the PLUGINS_CONFIG in older version of NTV)