openwisp-controller 1.2.3__tar.gz → 1.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (391) hide show
  1. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/CHANGES.rst +163 -0
  2. {openwisp_controller-1.2.3/openwisp_controller.egg-info → openwisp_controller-1.3.1}/PKG-INFO +15 -14
  3. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/__init__.py +1 -1
  4. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/admin.py +145 -31
  5. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/api/serializers.py +30 -1
  6. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/api/views.py +17 -19
  7. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/apps.py +128 -78
  8. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/base.py +3 -4
  9. openwisp_controller-1.3.1/openwisp_controller/config/base/cache.py +402 -0
  10. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/channels_consumer.py +1 -7
  11. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/config.py +153 -52
  12. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/device.py +74 -6
  13. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/device_group.py +5 -21
  14. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/multitenancy.py +21 -16
  15. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/template.py +4 -3
  16. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/vpn.py +113 -9
  17. openwisp_controller-1.3.1/openwisp_controller/config/base/whois.py +210 -0
  18. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/controller/views.py +53 -14
  19. openwisp_controller-1.3.1/openwisp_controller/config/converters.py +27 -0
  20. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/handlers.py +64 -15
  21. openwisp_controller-1.3.1/openwisp_controller/config/management/commands/clear_last_ip.py +26 -0
  22. openwisp_controller-1.3.1/openwisp_controller/config/management/commands/print_cache_dependencies.py +22 -0
  23. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0001_squashed_0002_config_settings_uuid.py +3 -11
  24. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0018_config_context.py +2 -6
  25. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0023_update_context.py +2 -6
  26. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0028_template_default_values.py +2 -6
  27. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0036_device_group.py +1 -5
  28. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0042_multiple_wireguard_tunnels.py +9 -4
  29. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0048_wifi_radio_band_migration.py +13 -6
  30. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0049_devicegroup_context.py +2 -6
  31. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0051_organizationconfigsettings_context.py +2 -6
  32. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0057_populate_vpnclient_template.py +8 -2
  33. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0059_zerotier_templates_ow_zt_to_global.py +10 -5
  34. openwisp_controller-1.3.1/openwisp_controller/config/migrations/0061_config_checksum_db.py +330 -0
  35. openwisp_controller-1.3.1/openwisp_controller/config/migrations/0062_whoisinfo.py +89 -0
  36. openwisp_controller-1.3.1/openwisp_controller/config/migrations/0063_replace_jsonfield_with_django_builtin.py +122 -0
  37. openwisp_controller-1.3.1/openwisp_controller/config/migrations/0064_template_notes.py +18 -0
  38. openwisp_controller-1.3.1/openwisp_controller/config/migrations/0065_whoisinfo_unreferenced_since.py +19 -0
  39. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/__init__.py +13 -1
  40. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/models.py +11 -0
  41. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/settings.py +20 -5
  42. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/signals.py +8 -0
  43. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/css/admin.css +48 -31
  44. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/css/lib/advanced-mode.css +70 -62
  45. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/css/lib/jsonschema-ui.css +18 -18
  46. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/lib/advanced-mode.js +1 -1
  47. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/management_ip.js +9 -1
  48. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/relevant_templates.js +19 -4
  49. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/tabs.js +5 -0
  50. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/widget.js +3 -3
  51. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/import_export/import-openwisp.css +7 -7
  52. openwisp_controller-1.3.1/openwisp_controller/config/static/whois/css/whois.css +110 -0
  53. openwisp_controller-1.3.1/openwisp_controller/config/static/whois/images/whois_globe.svg +3 -0
  54. openwisp_controller-1.3.1/openwisp_controller/config/static/whois/js/whois.js +62 -0
  55. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tasks.py +32 -22
  56. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tasks_zerotier.py +10 -58
  57. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/change_form.html +8 -0
  58. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/device/change_form.html +1 -1
  59. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/pytest.py +3 -3
  60. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_admin.py +297 -94
  61. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_api.py +112 -22
  62. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_config.py +619 -2
  63. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_controller.py +500 -316
  64. openwisp_controller-1.3.1/openwisp_controller/config/tests/test_converters.py +23 -0
  65. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_device.py +104 -67
  66. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_handlers.py +1 -1
  67. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_notifications.py +3 -1
  68. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_selenium.py +113 -40
  69. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_views.py +1 -1
  70. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_vpn.py +206 -29
  71. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/utils.py +43 -0
  72. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/utils.py +84 -21
  73. openwisp_controller-1.3.1/openwisp_controller/config/whois/commands.py +68 -0
  74. openwisp_controller-1.3.1/openwisp_controller/config/whois/handlers.py +45 -0
  75. openwisp_controller-1.3.1/openwisp_controller/config/whois/mixins.py +20 -0
  76. openwisp_controller-1.3.1/openwisp_controller/config/whois/serializers.py +17 -0
  77. openwisp_controller-1.3.1/openwisp_controller/config/whois/service.py +290 -0
  78. openwisp_controller-1.3.1/openwisp_controller/config/whois/tasks.py +138 -0
  79. openwisp_controller-1.3.1/openwisp_controller/config/whois/tests/tests.py +1353 -0
  80. openwisp_controller-1.3.1/openwisp_controller/config/whois/tests/utils.py +164 -0
  81. openwisp_controller-1.3.1/openwisp_controller/config/whois/utils.py +74 -0
  82. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/admin.py +26 -2
  83. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/api/urls.py +1 -1
  84. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/api/views.py +15 -13
  85. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/apps.py +6 -3
  86. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/base/models.py +85 -35
  87. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/channels/routing.py +3 -3
  88. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/commands.py +1 -1
  89. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/connectors/openwrt/ssh.py +1 -1
  90. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/connectors/ssh.py +3 -3
  91. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/0001_initial.py +6 -10
  92. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/0007_command.py +1 -5
  93. openwisp_controller-1.3.1/openwisp_controller/connection/migrations/0010_replace_jsonfield_with_django_builtin.py +47 -0
  94. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/static/connection/css/command-inline.css +24 -10
  95. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/static/connection/js/commands.js +27 -16
  96. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tasks.py +6 -3
  97. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/pytest.py +5 -4
  98. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/test_admin.py +37 -17
  99. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/test_api.py +4 -4
  100. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/test_models.py +98 -7
  101. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/test_notifications.py +6 -4
  102. openwisp_controller-1.3.1/openwisp_controller/connection/tests/test_selenium.py +126 -0
  103. openwisp_controller-1.3.1/openwisp_controller/connection/tests/test_tasks.py +317 -0
  104. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/utils.py +1 -1
  105. openwisp_controller-1.3.1/openwisp_controller/geo/admin.py +249 -0
  106. openwisp_controller-1.3.1/openwisp_controller/geo/api/filters.py +58 -0
  107. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/api/serializers.py +159 -44
  108. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/api/views.py +142 -17
  109. openwisp_controller-1.3.1/openwisp_controller/geo/apps.py +177 -0
  110. openwisp_controller-1.3.1/openwisp_controller/geo/base/models.py +219 -0
  111. openwisp_controller-1.3.1/openwisp_controller/geo/channels/consumers.py +42 -0
  112. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/channels/routing.py +11 -3
  113. openwisp_controller-1.3.1/openwisp_controller/geo/estimated_location/handlers.py +83 -0
  114. openwisp_controller-1.3.1/openwisp_controller/geo/estimated_location/mixins.py +37 -0
  115. openwisp_controller-1.3.1/openwisp_controller/geo/estimated_location/service.py +253 -0
  116. openwisp_controller-1.3.1/openwisp_controller/geo/estimated_location/tasks.py +47 -0
  117. openwisp_controller-1.3.1/openwisp_controller/geo/estimated_location/tests/tests.py +1415 -0
  118. openwisp_controller-1.3.1/openwisp_controller/geo/estimated_location/tests/utils.py +43 -0
  119. openwisp_controller-1.3.1/openwisp_controller/geo/estimated_location/utils.py +143 -0
  120. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/migrations/0001_initial.py +4 -1
  121. openwisp_controller-1.3.1/openwisp_controller/geo/migrations/0004_location_is_estimated.py +22 -0
  122. openwisp_controller-1.3.1/openwisp_controller/geo/migrations/0005_organizationgeosettings.py +55 -0
  123. openwisp_controller-1.3.1/openwisp_controller/geo/migrations/0006_create_geo_settings_for_existing_orgs.py +40 -0
  124. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/migrations/__init__.py +15 -2
  125. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/models.py +12 -1
  126. openwisp_controller-1.3.1/openwisp_controller/geo/settings.py +13 -0
  127. openwisp_controller-1.3.1/openwisp_controller/geo/tasks.py +3 -0
  128. openwisp_controller-1.3.1/openwisp_controller/geo/templates/admin/geo/device_location_inline.html +11 -0
  129. openwisp_controller-1.3.1/openwisp_controller/geo/templates/admin/geo/location/change_form.html +11 -0
  130. openwisp_controller-1.3.1/openwisp_controller/geo/tests/pytest.py +201 -0
  131. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/tests/test_admin.py +68 -0
  132. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/tests/test_admin_inline.py +11 -15
  133. openwisp_controller-1.3.1/openwisp_controller/geo/tests/test_api.py +2067 -0
  134. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/tests/test_selenium.py +42 -3
  135. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/tests/utils.py +6 -3
  136. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/utils.py +10 -0
  137. openwisp_controller-1.3.1/openwisp_controller/pki/__init__.py +0 -0
  138. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/api/views.py +5 -9
  139. openwisp_controller-1.3.1/openwisp_controller/pki/base/__init__.py +0 -0
  140. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0001_initial.py +2 -8
  141. openwisp_controller-1.3.1/openwisp_controller/pki/migrations/0012_alter_ca_extensions_alter_ca_key_length_and_more.py +70 -0
  142. openwisp_controller-1.3.1/openwisp_controller/pki/tests/__init__.py +0 -0
  143. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/tests/test_api.py +64 -51
  144. openwisp_controller-1.3.1/openwisp_controller/settings.py +8 -0
  145. openwisp_controller-1.3.1/openwisp_controller/subnet_division/__init__.py +0 -0
  146. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/admin.py +12 -8
  147. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/base/models.py +1 -1
  148. openwisp_controller-1.3.1/openwisp_controller/subnet_division/rule_types/__init__.py +0 -0
  149. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/rule_types/base.py +36 -16
  150. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/rule_types/vpn.py +5 -2
  151. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/static/subnet-division/css/subnet-division.css +3 -3
  152. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/tasks.py +9 -0
  153. openwisp_controller-1.3.1/openwisp_controller/subnet_division/tests/__init__.py +0 -0
  154. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/tests/test_admin.py +11 -1
  155. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/tests/test_models.py +103 -12
  156. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/tests/mixins.py +5 -0
  157. openwisp_controller-1.3.1/openwisp_controller/tests/test_migrationgraph.py +32 -0
  158. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/tests/test_selenium.py +5 -3
  159. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1/openwisp_controller.egg-info}/PKG-INFO +15 -14
  160. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller.egg-info/SOURCES.txt +45 -0
  161. openwisp_controller-1.3.1/openwisp_controller.egg-info/requires.txt +19 -0
  162. openwisp_controller-1.3.1/requirements.txt +19 -0
  163. openwisp_controller-1.2.3/openwisp_controller/config/migrations/0061_config_checksum_db.py +0 -57
  164. openwisp_controller-1.2.3/openwisp_controller/connection/tests/test_selenium.py +0 -69
  165. openwisp_controller-1.2.3/openwisp_controller/connection/tests/test_tasks.py +0 -137
  166. openwisp_controller-1.2.3/openwisp_controller/geo/admin.py +0 -142
  167. openwisp_controller-1.2.3/openwisp_controller/geo/api/filters.py +0 -27
  168. openwisp_controller-1.2.3/openwisp_controller/geo/apps.py +0 -119
  169. openwisp_controller-1.2.3/openwisp_controller/geo/base/models.py +0 -65
  170. openwisp_controller-1.2.3/openwisp_controller/geo/channels/consumers.py +0 -19
  171. openwisp_controller-1.2.3/openwisp_controller/geo/tests/pytest.py +0 -110
  172. openwisp_controller-1.2.3/openwisp_controller/geo/tests/test_api.py +0 -1056
  173. openwisp_controller-1.2.3/openwisp_controller/settings.py +0 -3
  174. openwisp_controller-1.2.3/openwisp_controller.egg-info/requires.txt +0 -18
  175. openwisp_controller-1.2.3/requirements.txt +0 -18
  176. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/LICENSE +0 -0
  177. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/MANIFEST.in +0 -0
  178. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/README.rst +0 -0
  179. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/admin.py +0 -0
  180. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/base.py +0 -0
  181. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/checks.py +0 -0
  182. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/__init__.py +0 -0
  183. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/api/__init__.py +0 -0
  184. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/api/download_views.py +0 -0
  185. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/api/filters.py +0 -0
  186. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/api/urls.py +0 -0
  187. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/api/zerotier_service.py +0 -0
  188. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/__init__.py +0 -0
  189. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/base/tag.py +0 -0
  190. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/controller/__init__.py +0 -0
  191. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/controller/urls.py +0 -0
  192. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/crypto.py +0 -0
  193. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/exceptions.py +0 -0
  194. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/exportable.py +0 -0
  195. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/filters.py +0 -0
  196. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/fixtures/test_templates.json +0 -0
  197. {openwisp_controller-1.2.3/openwisp_controller/config/sortedm2m → openwisp_controller-1.3.1/openwisp_controller/config/management}/__init__.py +0 -0
  198. {openwisp_controller-1.2.3/openwisp_controller/connection → openwisp_controller-1.3.1/openwisp_controller/config/management/commands}/__init__.py +0 -0
  199. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0003_template_tags.py +0 -0
  200. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0004_add_device_model.py +0 -0
  201. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0005_populate_device.py +0 -0
  202. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0006_config_device_not_null.py +0 -0
  203. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0007_simplify_config.py +0 -0
  204. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0008_update_indexes.py +0 -0
  205. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0009_device_system.py +0 -0
  206. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0010_auto_20180106_1814.py +0 -0
  207. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0011_update_device_mac_address.py +0 -0
  208. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0012_auto_20180219_1501.py +0 -0
  209. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0013_last_ip_management_ip_and_status_applied.py +0 -0
  210. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0014_device_hardware_id.py +0 -0
  211. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0015_default_groups_permissions.py +0 -0
  212. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0016_default_organization_config_settings.py +0 -0
  213. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0017_template_name_organization_unique_together.py +0 -0
  214. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0019_organization_mac_add_hardware_id_name_unique_together.py +0 -0
  215. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0020_remove_config_organization.py +0 -0
  216. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0021_vpn_key.py +0 -0
  217. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0022_vpn_format_dh.py +0 -0
  218. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0024_update_context_data.py +0 -0
  219. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0025_update_device_key.py +0 -0
  220. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0026_hardware_id_not_unique.py +0 -0
  221. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0027_add_indexes_on_ip_fields.py +0 -0
  222. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0029_merge_django_netjsonconfig.py +0 -0
  223. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0030_django_taggit_update.py +0 -0
  224. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0031_update_vpn_dh_param.py +0 -0
  225. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0032_update_legacy_vpn_backend.py +0 -0
  226. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0033_name_unique_per_organization.py +0 -0
  227. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0034_template_required.py +0 -0
  228. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0035_device_name_unique_optional.py +0 -0
  229. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0037_alter_taggedtemplate.py +0 -0
  230. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0038_vpn_subnet.py +0 -0
  231. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0039_wireguard_vxlan_ipam.py +0 -0
  232. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0040_vpnclient_ip_setnull.py +0 -0
  233. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0041_default_groups_organizationconfigsettings_permission.py +0 -0
  234. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0043_devicegroup_templates.py +0 -0
  235. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0044_config_error_reason.py +0 -0
  236. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0045_alter_vpn_webhook_endpoint.py +0 -0
  237. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0046_organizationlimits.py +0 -0
  238. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0047_add_organizationlimits.py +0 -0
  239. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0050_alter_vpnclient_unique_together.py +0 -0
  240. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0052_vpn_node_network_id.py +0 -0
  241. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0053_vpnclient_secret.py +0 -0
  242. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0054_device__is_deactivated.py +0 -0
  243. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0055_alter_config_status.py +0 -0
  244. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0056_vpnclient_template.py +0 -0
  245. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0058_alter_vpnclient_template.py +0 -0
  246. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/migrations/0060_cleanup_api_task_notification_types.py +0 -0
  247. {openwisp_controller-1.2.3/openwisp_controller/connection/base → openwisp_controller-1.3.1/openwisp_controller/config/sortedm2m}/__init__.py +0 -0
  248. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/sortedm2m/fields.py +0 -0
  249. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/sortedm2m/forms.py +0 -0
  250. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/css/device-delete-confirmation.css +0 -0
  251. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/css/devicegroup.css +0 -0
  252. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/css/lib/img/jsoneditor-icons.svg +0 -0
  253. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/device-delete-confirmation.js +0 -0
  254. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/lib/jsonschema-ui.js +0 -0
  255. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/lib/tomorrow_night_bright.js +0 -0
  256. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/preview.js +0 -0
  257. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/switcher.js +0 -0
  258. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/unsaved_changes.js +0 -0
  259. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/utils.js +0 -0
  260. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/config/js/vpn.js +0 -0
  261. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/sortedm2m/patch_sortedm2m.js +0 -0
  262. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/static/support.css +0 -0
  263. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/change_device_group.html +0 -0
  264. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/change_list_device.html +0 -0
  265. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/clone_template_form.html +0 -0
  266. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/device/delete_confirmation.html +0 -0
  267. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/device/delete_selected_confirmation.html +0 -0
  268. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/device_recover_form.html +0 -0
  269. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/jsonschema-widget.html +0 -0
  270. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/preview.html +0 -0
  271. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/config/system_context.html +0 -0
  272. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/device_group/change_form.html +0 -0
  273. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/admin/import_export/import.html +0 -0
  274. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/templates/reversion/config/revision_form.html +0 -0
  275. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/__init__.py +0 -0
  276. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_apps.py +0 -0
  277. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_device_group.py +0 -0
  278. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_tag.py +0 -0
  279. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/tests/test_template.py +0 -0
  280. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/urls.py +0 -0
  281. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/validators.py +0 -0
  282. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/views.py +0 -0
  283. {openwisp_controller-1.2.3/openwisp_controller/connection/connectors → openwisp_controller-1.3.1/openwisp_controller/config/whois}/__init__.py +0 -0
  284. {openwisp_controller-1.2.3/openwisp_controller/connection/connectors/openwrt → openwisp_controller-1.3.1/openwisp_controller/config/whois/tests}/__init__.py +0 -0
  285. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/config/widgets.py +0 -0
  286. {openwisp_controller-1.2.3/openwisp_controller/connection/tests → openwisp_controller-1.3.1/openwisp_controller/connection}/__init__.py +0 -0
  287. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/api/serializers.py +0 -0
  288. {openwisp_controller-1.2.3/openwisp_controller/geo → openwisp_controller-1.3.1/openwisp_controller/connection/base}/__init__.py +0 -0
  289. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/channels/consumers.py +0 -0
  290. {openwisp_controller-1.2.3/openwisp_controller/geo/api → openwisp_controller-1.3.1/openwisp_controller/connection/connectors}/__init__.py +0 -0
  291. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/connectors/airos/snmp.py +0 -0
  292. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/connectors/exceptions.py +0 -0
  293. {openwisp_controller-1.2.3/openwisp_controller/geo/base → openwisp_controller-1.3.1/openwisp_controller/connection/connectors/openwrt}/__init__.py +0 -0
  294. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/connectors/openwrt/snmp.py +0 -0
  295. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/connectors/snmp.py +0 -0
  296. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/exceptions.py +0 -0
  297. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/0002_credentials_auto_add.py +0 -0
  298. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/0003_default_group_permissions.py +0 -0
  299. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/0004_django3_1_upgrade.py +0 -0
  300. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/0005_device_connection_failure_reason.py +0 -0
  301. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/0006_name_unique_per_organization.py +0 -0
  302. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/0008_remove_conflicting_deviceconnections.py +0 -0
  303. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/0009_alter_deviceconnection_unique_together.py +0 -0
  304. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/migrations/__init__.py +0 -0
  305. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/models.py +0 -0
  306. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/schema.py +0 -0
  307. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/settings.py +0 -0
  308. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/signals.py +0 -0
  309. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/static/connection/css/credentials.css +0 -0
  310. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/static/connection/js/credentials.js +0 -0
  311. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/static/connection/js/lib/reconnecting-websocket.min.js +0 -0
  312. {openwisp_controller-1.2.3/openwisp_controller/geo/channels → openwisp_controller-1.3.1/openwisp_controller/connection/tests}/__init__.py +0 -0
  313. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/base.py +0 -0
  314. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/test-key.ed25519 +0 -0
  315. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/test-key.rsa +0 -0
  316. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/test_command_utilities.py +0 -0
  317. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/test_snmp.py +0 -0
  318. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/tests/test_ssh.py +0 -0
  319. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/utils.py +0 -0
  320. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/connection/widgets.py +0 -0
  321. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/context_processors.py +0 -0
  322. {openwisp_controller-1.2.3/openwisp_controller/geo/tests → openwisp_controller-1.3.1/openwisp_controller/geo}/__init__.py +0 -0
  323. {openwisp_controller-1.2.3/openwisp_controller/pki → openwisp_controller-1.3.1/openwisp_controller/geo/api}/__init__.py +0 -0
  324. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/api/urls.py +0 -0
  325. {openwisp_controller-1.2.3/openwisp_controller/pki → openwisp_controller-1.3.1/openwisp_controller/geo}/base/__init__.py +0 -0
  326. {openwisp_controller-1.2.3/openwisp_controller/pki/tests → openwisp_controller-1.3.1/openwisp_controller/geo/channels}/__init__.py +0 -0
  327. {openwisp_controller-1.2.3/openwisp_controller/subnet_division → openwisp_controller-1.3.1/openwisp_controller/geo/estimated_location}/__init__.py +0 -0
  328. {openwisp_controller-1.2.3/openwisp_controller/subnet_division/rule_types → openwisp_controller-1.3.1/openwisp_controller/geo/estimated_location/tests}/__init__.py +0 -0
  329. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/exportable.py +0 -0
  330. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/migrations/0002_default_groups_permissions.py +0 -0
  331. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/migrations/0003_alter_devicelocation_floorplan_location.py +0 -0
  332. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/templates/admin/widgets/foreign_key_raw_id.html +0 -0
  333. {openwisp_controller-1.2.3/openwisp_controller/subnet_division → openwisp_controller-1.3.1/openwisp_controller/geo}/tests/__init__.py +0 -0
  334. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/tests/test_apps.py +0 -0
  335. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/geo/tests/test_models.py +0 -0
  336. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/migrations.py +0 -0
  337. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/mixins.py +0 -0
  338. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/admin.py +0 -0
  339. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/api/serializers.py +0 -0
  340. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/api/urls.py +0 -0
  341. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/apps.py +0 -0
  342. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/base/models.py +0 -0
  343. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0002_add_organization_name.py +0 -0
  344. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0003_fill_organization_name.py +0 -0
  345. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0004_auto_20180106_1814.py +0 -0
  346. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0005_organizational_unit_name.py +0 -0
  347. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0006_add_x509_passphrase_field.py +0 -0
  348. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0007_default_groups_permissions.py +0 -0
  349. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0008_serial_number_length.py +0 -0
  350. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0009_common_name_maxlength_64.py +0 -0
  351. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0010_common_name_organization_unique.py +0 -0
  352. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/0011_disallowed_blank_key_length_or_digest.py +0 -0
  353. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/migrations/__init__.py +0 -0
  354. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/models.py +0 -0
  355. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/static/admin/pki/js/show-org-field.js +0 -0
  356. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/templates/admin/pki/change_form.html +0 -0
  357. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/tests/test_admin.py +0 -0
  358. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/tests/test_models.py +0 -0
  359. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/tests/utils.py +0 -0
  360. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/urls.py +0 -0
  361. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/pki/utils.py +0 -0
  362. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/routing.py +0 -0
  363. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/serializers.py +0 -0
  364. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/apps.py +0 -0
  365. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/filters.py +0 -0
  366. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/migrations/0001_initial.py +0 -0
  367. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/migrations/0002_default_group_migration.py +0 -0
  368. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/migrations/0003_related_field_allow_blank.py +0 -0
  369. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/migrations/0004_index_rule_on_delete.py +0 -0
  370. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/migrations/0005_number_of_subnets_and_ips.py +0 -0
  371. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/migrations/__init__.py +0 -0
  372. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/models.py +0 -0
  373. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/rule_types/device.py +0 -0
  374. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/settings.py +0 -0
  375. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/signals.py +0 -0
  376. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/static/subnet-division/js/subnet-division.js +0 -0
  377. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/tests/helpers.py +0 -0
  378. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/tests/test_rule.py +0 -0
  379. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/subnet_division/utils.py +0 -0
  380. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/tests/__init__.py +0 -0
  381. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/tests/test_users_integration.py +0 -0
  382. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/tests/test_utilities.py +0 -0
  383. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/tests/utils.py +0 -0
  384. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/urls.py +0 -0
  385. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller/vpn_backends.py +0 -0
  386. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller.egg-info/dependency_links.txt +0 -0
  387. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller.egg-info/not-zip-safe +0 -0
  388. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/openwisp_controller.egg-info/top_level.txt +0 -0
  389. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/pyproject.toml +0 -0
  390. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/setup.cfg +0 -0
  391. {openwisp_controller-1.2.3 → openwisp_controller-1.3.1}/setup.py +0 -0
@@ -1,6 +1,169 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ Version 1.3.1 [2026-10-15]
5
+ --------------------------
6
+
7
+ Security
8
+ ~~~~~~~~
9
+
10
+ - Fixed insecure multi-tenancy boundary in location REST API
11
+
12
+ Fixed a **security issue** that allowed organization managers to link
13
+ locations and floorplans belonging to other organizations to their
14
+ devices by submitting known object IDs.
15
+
16
+ The API now resolves related objects only from organizations managed by
17
+ the requesting user and validates the organization of all
18
+ device-location relationships. Device lists for a location are scoped to
19
+ managed organizations and no longer expose device keys.
20
+
21
+ Nested location and floorplan writes are validated before being saved
22
+ and run in a database transaction, preventing rejected requests from
23
+ leaving related database objects or files behind.
24
+
25
+ Advisory: **GHSA-39c4-xj33-j8j3**.
26
+
27
+ - Fixed XSS vulnerabilities in device and command admin UI
28
+
29
+ Fixed cross-site scripting (XSS) vulnerabilities caused by improper
30
+ escaping of user-controlled data in the following admin pages:
31
+
32
+ - Device and device group forms (template names).
33
+ - Device pages (command inputs, outputs, and management IP).
34
+ - Subnet administration (device names and links).
35
+
36
+ These vulnerabilities could allow attackers to execute malicious
37
+ JavaScript in an administrator's browser.
38
+
39
+ Advisory: **GHSA-4f2x-pxjx-h8m3**.
40
+
41
+ Bugfixes
42
+ ~~~~~~~~
43
+
44
+ - Fixed loss of data in PATCH requests to device location endpoint `#1510
45
+ <https://github.com/openwisp/openwisp-controller/issues/1510>`_
46
+
47
+ Sending `PATCH` requests to the device location endpoint
48
+ (`/api/v1/controller/device/{id}/location/`) now doesn't set `location`
49
+ and `floorplan` to `NULL` when they are not included in the request
50
+ body.
51
+
52
+ Changes
53
+ ~~~~~~~
54
+
55
+ - Added IP address search to DeviceAdmin
56
+
57
+ Enabled searches by last and management IP addresses.
58
+
59
+ Version 1.3.0 [2026-09-03]
60
+ --------------------------
61
+
62
+ Features
63
+ ~~~~~~~~
64
+
65
+ - Added `WHOIS lookup and geographic location estimation
66
+ <https://github.com/openwisp/openwisp-controller/issues/1034>`_ for
67
+ devices, including WHOIS details in the admin UI, automatic refresh of
68
+ stale WHOIS data, estimated-location notifications and warnings, and
69
+ admin and API filters for estimated locations.
70
+ - Added `WebSocket support for broadcasting all location updates
71
+ <https://github.com/openwisp/openwisp-controller/issues/1157>`_.
72
+ - Added an `API endpoint for retrieving indoor device coordinates
73
+ <https://github.com/openwisp/openwisp-controller/issues/828>`_ for a
74
+ location.
75
+ - Added `location and floorplan filters to the device list API
76
+ <https://github.com/openwisp/openwisp-controller/issues/1158>`_,
77
+ allowing searches by location name and filtering by location or
78
+ floorplan UUID.
79
+ - Added `generic cache invalidation for configuration models
80
+ <https://github.com/openwisp/openwisp-controller/issues/1095>`_. Models
81
+ can declare cache dependencies using ``CacheDependency``, and the new
82
+ ``print_cache_dependencies`` management command can be used to inspect
83
+ the dependency graph.
84
+ - Added `notes to configuration templates
85
+ <https://github.com/openwisp/openwisp-controller/issues/1412>`_.
86
+ - Allowed devices to update their name during re-registration.
87
+
88
+ Changes
89
+ ~~~~~~~
90
+
91
+ Other changes
92
+ +++++++++++++
93
+
94
+ - Enforced the `deactivated device state across OpenWISP Controller
95
+ <https://github.com/openwisp/openwisp-controller/issues/1338>`_.
96
+ Deactivated devices can no longer register, receive configuration
97
+ updates, run remote commands, or receive automatically assigned
98
+ templates. Cleanup and security operations continue to work normally.
99
+ - Added `VPN subnet and IP address details to the REST API
100
+ <https://github.com/openwisp/openwisp-controller/issues/1003>`_.
101
+ - Allowed deleting organizations containing active devices while
102
+ displaying a warning about the active devices that will be affected.
103
+ - Replaced the third-party ``jsonfield`` implementation with Django's
104
+ built-in ``JSONField`` `#1061
105
+ <https://github.com/openwisp/openwisp-controller/issues/1061>`_,
106
+ including migration handling for databases upgraded from older OpenWISP
107
+ Controller versions.
108
+ - Changed VPN synchronization errors and recovery events to use
109
+ ``generic_message`` notifications `#1049
110
+ <https://github.com/openwisp/openwisp-controller/issues/1049>`_.
111
+ - Improved command status visibility in the admin interface by
112
+ highlighting statuses with different colors `#812
113
+ <https://github.com/openwisp/openwisp-controller/issues/812>`_.
114
+
115
+ Dependencies
116
+ ++++++++++++
117
+
118
+ - Bumped ``django-reversion`` from ``~=6.0.0`` to `~=6.3.0
119
+ <https://github.com/etianen/django-reversion/blob/v6.3.0/CHANGELOG.rst>`_.
120
+ - Bumped ``netjsonconfig`` from ``~=1.2.0`` to `~=1.3.0
121
+ <https://github.com/openwisp/netjsonconfig/blob/master/CHANGES.rst>`__.
122
+ - Bumped ``django-x509`` from ``~=1.3.0`` to `~=1.4.0
123
+ <https://github.com/openwisp/django-x509/blob/master/CHANGES.rst>`_.
124
+ - Bumped ``django-loci`` from ``~=1.2.0`` to `~=1.3.0
125
+ <https://github.com/openwisp/django-loci/blob/master/CHANGES.rst>`__.
126
+ - Bumped ``django-flat-json-widget`` from ``~=0.4.0`` to `~=0.5.0
127
+ <https://github.com/openwisp/django-flat-json-widget/blob/master/CHANGES.rst>`_.
128
+ - Bumped ``openwisp-users`` from ``~=1.2.0`` to `~=1.3.0
129
+ <https://github.com/openwisp/openwisp-users/blob/master/CHANGES.rst>`__.
130
+ - Bumped ``openwisp-utils[celery,channels]`` from ``~=1.2.0`` to `~=1.3.0
131
+ <https://github.com/openwisp/openwisp-utils/blob/master/CHANGES.rst>`__.
132
+ - Bumped ``openwisp-notifications`` from ``~=1.2.0`` to `~=1.3.0
133
+ <https://github.com/openwisp/openwisp-notifications/blob/master/CHANGES.rst>`__.
134
+ - Bumped ``openwisp-ipam`` from ``~=1.2.0`` to `~=1.3.1
135
+ <https://github.com/openwisp/openwisp-ipam/blob/master/CHANGES.rst>`_.
136
+ - Bumped ``djangorestframework-gis`` from ``~=1.2.0`` to `~=1.3.0
137
+ <https://github.com/openwisp/django-rest-framework-gis/blob/master/CHANGES.rst>`__.
138
+ - Bumped ``paramiko`` from ``~=4.0.0`` to `~=5.0.0
139
+ <https://www.paramiko.org/changelog.html>`_.
140
+ - Bumped ``scp`` from ``~=0.15.0`` to `~=0.16.1
141
+ <https://github.com/jbardin/scp.py/releases/tag/v0.16.1>`_.
142
+ - Bumped ``django-import-export`` from ``~=4.3.10`` to `~=4.4.1
143
+ <https://django-import-export.readthedocs.io/en/latest/changelog.html>`_.
144
+ - Added `geoip2>=5.3.0,<6.0.0
145
+ <https://github.com/maxmind/GeoIP2-python/releases/tag/v5.3.0>`_.
146
+ - Dropped support for Django ``4.2``.
147
+ - Dropped support for Python ``3.9``.
148
+
149
+ Bugfixes
150
+ ~~~~~~~~
151
+
152
+ - Fixed `subnet-division rules allocating addresses already in use
153
+ <https://github.com/openwisp/openwisp-controller/issues/1464>`_ by
154
+ respecting existing child prefixes and pre-allocated IP addresses.
155
+ - Fixed background command and connection operations potentially restoring
156
+ objects that had been deleted concurrently or causing foreign key
157
+ errors.
158
+ - Fixed VPN client synchronization when switching VPN templates,
159
+ preventing inconsistent client state during repeated template updates.
160
+ - Fixed SSH connections remaining open when ``update_config()`` failed.
161
+ - Fixed `malformed UUID URLs returning HTTP 500 instead of HTTP 404
162
+ <https://github.com/openwisp/openwisp-controller/issues/682>`_.
163
+ - Fixed the Device admin delete confirmation page when OpenWISP Controller
164
+ is extended by downstream applications.
165
+ - Fixed a typo in the ``update_strategy`` validation error message.
166
+
4
167
  Version 1.2.3 [2026-04-09]
5
168
  --------------------------
6
169
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openwisp-controller
3
- Version: 1.2.3
3
+ Version: 1.3.1
4
4
  Summary: OpenWISP Controller
5
5
  Home-page: http://openwisp.org
6
6
  Download-URL: https://github.com/openwisp/openwisp-controller/releases
@@ -20,23 +20,24 @@ Classifier: Framework :: Django
20
20
  Classifier: Programming Language :: Python :: 3
21
21
  License-File: LICENSE
22
22
  Requires-Dist: django-sortedm2m~=4.0.0
23
- Requires-Dist: django-reversion~=6.0.0
23
+ Requires-Dist: django-reversion~=6.3.0
24
24
  Requires-Dist: django-taggit~=6.1.0
25
- Requires-Dist: netjsonconfig~=1.2.0
26
- Requires-Dist: django-x509~=1.3.0
27
- Requires-Dist: django-loci~=1.2.0
28
- Requires-Dist: django-flat-json-widget~=0.4.0
29
- Requires-Dist: openwisp-users~=1.2.0
30
- Requires-Dist: openwisp-utils[celery,channels]~=1.2.0
31
- Requires-Dist: openwisp-notifications~=1.2.0
32
- Requires-Dist: openwisp-ipam~=1.2.0
33
- Requires-Dist: djangorestframework-gis~=1.2.0
34
- Requires-Dist: paramiko~=4.0.0
35
- Requires-Dist: scp~=0.15.0
25
+ Requires-Dist: netjsonconfig~=1.3.0
26
+ Requires-Dist: django-x509~=1.4.0
27
+ Requires-Dist: django-loci~=1.3.0
28
+ Requires-Dist: django-flat-json-widget~=0.5.0
29
+ Requires-Dist: openwisp-users~=1.3.0
30
+ Requires-Dist: openwisp-utils[celery,channels]~=1.3.0
31
+ Requires-Dist: openwisp-notifications~=1.3.0
32
+ Requires-Dist: openwisp-ipam~=1.3.1
33
+ Requires-Dist: djangorestframework-gis~=1.3.0
34
+ Requires-Dist: paramiko~=5.0.0
35
+ Requires-Dist: scp~=0.16.1
36
36
  Requires-Dist: django-cache-memoize~=0.2.1
37
37
  Requires-Dist: shortuuid~=1.0.13
38
38
  Requires-Dist: netaddr~=1.3.0
39
- Requires-Dist: django-import-export~=4.3.10
39
+ Requires-Dist: django-import-export~=4.4.1
40
+ Requires-Dist: geoip2<6.0.0,>=5.3.0
40
41
  Dynamic: author
41
42
  Dynamic: author-email
42
43
  Dynamic: classifier
@@ -1,4 +1,4 @@
1
- VERSION = (1, 2, 3, "final")
1
+ VERSION = (1, 3, 1, "final")
2
2
  __version__ = VERSION # alias
3
3
 
4
4
 
@@ -15,12 +15,14 @@ from django.core.exceptions import (
15
15
  ObjectDoesNotExist,
16
16
  ValidationError,
17
17
  )
18
+ from django.db import models
19
+ from django.db.models.functions import Cast
18
20
  from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse
19
21
  from django.http.response import HttpResponseForbidden
20
22
  from django.shortcuts import get_object_or_404
21
23
  from django.template.loader import get_template
22
24
  from django.template.response import TemplateResponse
23
- from django.urls import path, re_path, reverse
25
+ from django.urls import NoReverseMatch, path, reverse
24
26
  from django.utils.html import format_html, mark_safe
25
27
  from django.utils.translation import gettext_lazy as _
26
28
  from django.utils.translation import ngettext_lazy
@@ -31,12 +33,14 @@ from openwisp_ipam.filters import SubnetFilter
31
33
  from swapper import load_model
32
34
 
33
35
  from openwisp_controller.config.views import get_default_values, get_relevant_templates
36
+ from openwisp_controller.config.whois.utils import get_whois_info
34
37
  from openwisp_users.admin import OrganizationAdmin
35
38
  from openwisp_users.multitenancy import MultitenantOrgFilter
36
39
  from openwisp_utils.admin import (
37
40
  AlwaysHasChangedMixin,
41
+ BlockDeleteAllowCascadeMixin,
42
+ CopyableFieldsAdmin,
38
43
  TimeReadonlyAdminMixin,
39
- UUIDAdmin,
40
44
  )
41
45
 
42
46
  from ..admin import MultitenantAdminMixin
@@ -49,6 +53,7 @@ from .widgets import DeviceGroupJsonSchemaWidget, JsonSchemaWidget
49
53
 
50
54
  logger = logging.getLogger(__name__)
51
55
  prefix = "config/"
56
+ whois_prefix = "whois/"
52
57
  Config = load_model("config", "Config")
53
58
  Device = load_model("config", "Device")
54
59
  DeviceGroup = load_model("config", "DeviceGroup")
@@ -111,7 +116,7 @@ class BaseConfigAdmin(BaseAdmin):
111
116
 
112
117
  class Media:
113
118
  css = {"all": (f"{prefix}css/admin.css",)}
114
- js = list(UUIDAdmin.Media.js) + [
119
+ js = list(CopyableFieldsAdmin.Media.js) + [
115
120
  f"{prefix}js/{file_}"
116
121
  for file_ in ("preview.js", "unsaved_changes.js", "switcher.js")
117
122
  ]
@@ -137,16 +142,15 @@ class BaseConfigAdmin(BaseAdmin):
137
142
  if not issubclass(self.model, AbstractVpn):
138
143
  ctx["CONFIG_BACKEND_FIELD_SHOWN"] = app_settings.CONFIG_BACKEND_FIELD_SHOWN
139
144
  if pk:
140
- ctx["download_url"] = reverse("{0}_download".format(prefix), args=[pk])
141
145
  try:
146
+ download_url = reverse("{0}_download".format(prefix), args=[pk])
142
147
  has_config = True
143
148
  if self.model.__name__ == "Device":
144
149
  has_config = self.model.objects.get(pk=pk)._has_config()
145
- except (ObjectDoesNotExist, ValidationError):
150
+ except (ObjectDoesNotExist, ValidationError, NoReverseMatch):
146
151
  raise Http404()
147
152
  else:
148
- if not has_config:
149
- ctx["download_url"] = None
153
+ ctx["download_url"] = download_url if has_config else None
150
154
  return ctx
151
155
 
152
156
  def add_view(self, request, form_url="", extra_context=None):
@@ -167,8 +171,8 @@ class BaseConfigAdmin(BaseAdmin):
167
171
  options = getattr(self.model, "_meta")
168
172
  url_prefix = "{0}_{1}".format(options.app_label, options.model_name)
169
173
  return [
170
- re_path(
171
- r"^download/(?P<pk>[^/]+)/$",
174
+ path(
175
+ "download/<uuid_any:pk>/",
172
176
  self.admin_site.admin_view(self.download_view),
173
177
  name="{0}_download".format(url_prefix),
174
178
  ),
@@ -177,8 +181,8 @@ class BaseConfigAdmin(BaseAdmin):
177
181
  self.admin_site.admin_view(self.preview_view),
178
182
  name="{0}_preview".format(url_prefix),
179
183
  ),
180
- re_path(
181
- r"^(?P<pk>[^/]+)/context\.json$",
184
+ path(
185
+ "<uuid_any:pk>/context.json",
182
186
  self.admin_site.admin_view(self.context_view),
183
187
  name="{0}_context".format(url_prefix),
184
188
  ),
@@ -215,6 +219,15 @@ class BaseConfigAdmin(BaseAdmin):
215
219
  key = "{relation}_id".format(relation=key)
216
220
  # pass non-empty string or None
217
221
  kwargs[key] = value or None
222
+ # parse JSON strings for JSONField fields
223
+ elif isinstance(field, models.JSONField) and isinstance(value, str):
224
+ if not value:
225
+ kwargs[key] = None
226
+ else:
227
+ try:
228
+ kwargs[key] = json.loads(value)
229
+ except json.JSONDecodeError:
230
+ kwargs[key] = value
218
231
  # put regular field values in kwargs dict
219
232
  else:
220
233
  kwargs[key] = value
@@ -498,11 +511,12 @@ class ChangeDeviceGroupForm(forms.Form):
498
511
  )
499
512
 
500
513
 
501
- class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
514
+ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, CopyableFieldsAdmin):
502
515
  change_form_template = "admin/config/device/change_form.html"
503
516
  delete_selected_confirmation_template = (
504
517
  "admin/config/device/delete_selected_confirmation.html"
505
518
  )
519
+ delete_confirmation_template = "admin/config/device/delete_confirmation.html"
506
520
  list_display = [
507
521
  "name",
508
522
  "backend",
@@ -528,6 +542,8 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
528
542
  "model",
529
543
  "os",
530
544
  "system",
545
+ "last_ip",
546
+ "management_ip",
531
547
  "devicelocation__location__address",
532
548
  ]
533
549
  readonly_fields = ["last_ip", "uuid"]
@@ -548,6 +564,7 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
548
564
  "created",
549
565
  "modified",
550
566
  ]
567
+ copyable_fields = ["uuid"]
551
568
  inlines = [ConfigInline]
552
569
  conditional_inlines = []
553
570
  actions = [
@@ -569,12 +586,21 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
569
586
  fields.insert(0, "hardware_id")
570
587
  list_select_related = ("config", "organization")
571
588
 
572
- class Media(BaseConfigAdmin.Media):
589
+ # overriding media property to allow testing in isolation
590
+ # as class Media is evaluated at import time making the
591
+ # settings not overridable in tests
592
+ @property
593
+ def media(self):
573
594
  js = BaseConfigAdmin.Media.js + [
574
595
  f"{prefix}js/tabs.js",
575
596
  f"{prefix}js/management_ip.js",
576
597
  f"{prefix}js/relevant_templates.js",
577
598
  ]
599
+ css = BaseConfigAdmin.Media.css["all"]
600
+ if app_settings.WHOIS_CONFIGURED:
601
+ js.append(f"{whois_prefix}js/whois.js")
602
+ css += (f"{whois_prefix}css/whois.css",)
603
+ return super().media + forms.Media(js=js, css={"all": css})
578
604
 
579
605
  def has_change_permission(self, request, obj=None):
580
606
  perm = super().has_change_permission(request)
@@ -586,8 +612,70 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
586
612
  perm = super().has_delete_permission(request)
587
613
  if not obj:
588
614
  return perm
615
+ if BlockDeleteAllowCascadeMixin.is_admin_cascade_delete_request(self, request):
616
+ self._add_active_device_delete_warning(request, obj)
617
+ return perm
589
618
  return perm and obj.is_deactivated()
590
619
 
620
+ def _add_active_device_delete_warning(self, request, obj):
621
+ if getattr(request, "_active_device_delete_warning_checked", False):
622
+ return
623
+ resolver_match = getattr(request, "resolver_match", None)
624
+ url_name = getattr(resolver_match, "url_name", None)
625
+ opts = Organization._meta
626
+ organization_delete_url_name = f"{opts.app_label}_{opts.model_name}_delete"
627
+ organization_changelist_url_name = (
628
+ f"{opts.app_label}_{opts.model_name}_changelist"
629
+ )
630
+ active_organizations = 0
631
+ if request.method == "GET" and url_name == organization_delete_url_name:
632
+ request._active_device_delete_warning_checked = True
633
+ active_organizations = (
634
+ Device.objects.filter(
635
+ organization_id=obj.organization_id,
636
+ _is_deactivated=False,
637
+ )
638
+ .values("organization_id")
639
+ .distinct()
640
+ .count()
641
+ )
642
+ elif (
643
+ url_name == organization_changelist_url_name
644
+ and request.POST.get("action") == "delete_selected"
645
+ and not request.POST.get("post")
646
+ ):
647
+ request._active_device_delete_warning_checked = True
648
+ organizations = self.admin_site.get_model_admin(Organization).get_queryset(
649
+ request
650
+ )
651
+ active_organizations = (
652
+ Device.objects.filter(
653
+ organization_id__in=organizations.filter(
654
+ pk__in=request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
655
+ ),
656
+ _is_deactivated=False,
657
+ )
658
+ .values("organization_id")
659
+ .distinct()
660
+ .count()
661
+ )
662
+ if not active_organizations:
663
+ return
664
+ self.message_user(
665
+ request,
666
+ ngettext_lazy(
667
+ "This organization contains active devices. It is highly "
668
+ "recommended to deactivate them before deleting the organization "
669
+ "to ensure sensitive configuration data is disposed of safely.",
670
+ "%(count)d organizations contain active devices. It is highly "
671
+ "recommended to deactivate them before deleting the organizations "
672
+ "to ensure sensitive configuration data is disposed of safely.",
673
+ active_organizations,
674
+ )
675
+ % {"count": active_organizations},
676
+ messages.WARNING,
677
+ )
678
+
591
679
  def save_form(self, request, form, change):
592
680
  self._state_adding = form.instance._state.adding
593
681
  return super().save_form(request, form, change)
@@ -656,6 +744,7 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
656
744
  form = ChangeDeviceGroupForm(data=request.POST, org_id=org_id)
657
745
  if form.is_valid():
658
746
  group = form.cleaned_data["device_group"]
747
+ queryset = queryset.filter(_is_deactivated=False)
659
748
  # Evaluate queryset to store old group id
660
749
  old_group_qs = list(queryset)
661
750
  queryset.update(group=group or None)
@@ -683,8 +772,7 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
683
772
  "action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
684
773
  "opts": self.model._meta,
685
774
  "changelist_url": (
686
- f"{request.resolver_match.app_name}:"
687
- f"{request.resolver_match.url_name}"
775
+ f"{request.resolver_match.app_name}:{request.resolver_match.url_name}"
688
776
  ),
689
777
  }
690
778
 
@@ -928,6 +1016,10 @@ class DeviceAdmin(MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin):
928
1016
  ),
929
1017
  }
930
1018
  )
1019
+ # passing the whois details to the context to avoid
1020
+ # the need to make an additional request in the js
1021
+ if data := get_whois_info(pk):
1022
+ ctx["device_whois_details"] = data
931
1023
  return ctx
932
1024
 
933
1025
  def add_view(self, request, form_url="", extra_context=None):
@@ -1046,7 +1138,7 @@ class TemplateAdmin(MultitenantAdminMixin, BaseConfigAdmin, SystemDefinedVariabl
1046
1138
  "required",
1047
1139
  "created",
1048
1140
  ]
1049
- search_fields = ["name"]
1141
+ search_fields = ["name", "notes"]
1050
1142
  multitenant_shared_relations = ("vpn",)
1051
1143
  fields = [
1052
1144
  "name",
@@ -1058,6 +1150,7 @@ class TemplateAdmin(MultitenantAdminMixin, BaseConfigAdmin, SystemDefinedVariabl
1058
1150
  "tags",
1059
1151
  "default",
1060
1152
  "required",
1153
+ "notes",
1061
1154
  "system_context",
1062
1155
  "default_values",
1063
1156
  "config",
@@ -1089,8 +1182,7 @@ class TemplateAdmin(MultitenantAdminMixin, BaseConfigAdmin, SystemDefinedVariabl
1089
1182
  validated_org = Organization.objects.get(pk=organization)
1090
1183
  except (ValidationError, Organization.DoesNotExist) as e:
1091
1184
  logger.warning(
1092
- "Detected tampering in clone template "
1093
- f"form by user {user}: {e}"
1185
+ f"Detected tampering in clone template form by user {user}: {e}"
1094
1186
  )
1095
1187
  return
1096
1188
  if not user.is_superuser and not user.is_manager(organization):
@@ -1202,7 +1294,10 @@ class VpnForm(forms.ModelForm):
1202
1294
 
1203
1295
 
1204
1296
  class VpnAdmin(
1205
- MultitenantAdminMixin, BaseConfigAdmin, UUIDAdmin, SystemDefinedVariableMixin
1297
+ MultitenantAdminMixin,
1298
+ BaseConfigAdmin,
1299
+ CopyableFieldsAdmin,
1300
+ SystemDefinedVariableMixin,
1206
1301
  ):
1207
1302
  form = VpnForm
1208
1303
  list_display = [
@@ -1223,6 +1318,7 @@ class VpnAdmin(
1223
1318
  ]
1224
1319
  search_fields = ["id", "name", "host", "key"]
1225
1320
  readonly_fields = ["id", "uuid", "system_context"]
1321
+ copyable_fields = ["uuid"]
1226
1322
  multitenant_shared_relations = ("ca", "cert", "subnet")
1227
1323
  autocomplete_fields = ["ip", "subnet"]
1228
1324
  fields = [
@@ -1293,12 +1389,19 @@ class DeviceGroupAdmin(MultitenantAdminMixin, BaseAdmin):
1293
1389
  "created",
1294
1390
  "modified",
1295
1391
  ]
1296
- search_fields = ["name", "description", "meta_data"]
1392
+ search_fields = ["name", "description", "_meta_data_text"]
1297
1393
  list_filter = [MultitenantOrgFilter, DeviceGroupFilter]
1298
1394
  multitenant_shared_relations = ("templates",)
1299
1395
 
1396
+ def get_search_results(self, request, queryset, search_term):
1397
+ if search_term:
1398
+ queryset = queryset.annotate(
1399
+ _meta_data_text=Cast("meta_data", output_field=models.TextField()),
1400
+ )
1401
+ return super().get_search_results(request, queryset, search_term)
1402
+
1300
1403
  class Media:
1301
- js = list(UUIDAdmin.Media.js) + [
1404
+ js = list(CopyableFieldsAdmin.Media.js) + [
1302
1405
  f"{prefix}js/relevant_templates.js",
1303
1406
  ]
1304
1407
  css = {"all": (f"{prefix}css/admin.css",)}
@@ -1362,18 +1465,29 @@ class OrganizationLimitsInline(admin.StackedInline):
1362
1465
 
1363
1466
 
1364
1467
  limits_inline_position = 0
1365
- if getattr(app_settings, "REGISTRATION_ENABLED", True):
1366
1468
 
1367
- class ConfigSettingsForm(AlwaysHasChangedMixin, forms.ModelForm):
1368
- class Meta:
1369
- widgets = {"context": FlatJsonWidget}
1370
1469
 
1371
- class ConfigSettingsInline(admin.StackedInline):
1372
- model = OrganizationConfigSettings
1373
- form = ConfigSettingsForm
1470
+ class ConfigSettingsForm(AlwaysHasChangedMixin, forms.ModelForm):
1471
+ class Meta:
1472
+ widgets = {"context": FlatJsonWidget}
1473
+
1474
+
1475
+ class ConfigSettingsInline(admin.StackedInline):
1476
+ model = OrganizationConfigSettings
1477
+ form = ConfigSettingsForm
1478
+
1479
+ def get_fields(self, request, obj=None):
1480
+ fields = []
1481
+ if app_settings.REGISTRATION_ENABLED:
1482
+ fields += ["registration_enabled", "shared_secret"]
1483
+ if app_settings.WHOIS_CONFIGURED:
1484
+ fields += ["whois_enabled"]
1485
+ fields += ["context"]
1486
+ return fields
1487
+
1374
1488
 
1375
- OrganizationAdmin.save_on_top = True
1376
- OrganizationAdmin.inlines.insert(0, ConfigSettingsInline)
1377
- limits_inline_position = 1
1489
+ OrganizationAdmin.save_on_top = True
1490
+ OrganizationAdmin.inlines.insert(0, ConfigSettingsInline)
1491
+ limits_inline_position = 1
1378
1492
 
1379
1493
  OrganizationAdmin.inlines.insert(limits_inline_position, OrganizationLimitsInline)
@@ -9,6 +9,7 @@ from openwisp_utils.api.serializers import ValidatedModelSerializer
9
9
 
10
10
  from ...serializers import BaseSerializer
11
11
  from .. import settings as app_settings
12
+ from ..whois.mixins import WHOISMixin
12
13
 
13
14
  Template = load_model("config", "Template")
14
15
  Vpn = load_model("config", "Vpn")
@@ -40,6 +41,7 @@ class TemplateSerializer(BaseSerializer):
40
41
  "tags",
41
42
  "default",
42
43
  "required",
44
+ "notes",
43
45
  "default_values",
44
46
  "config",
45
47
  "created",
@@ -70,6 +72,7 @@ class TemplateSerializer(BaseSerializer):
70
72
 
71
73
  class VpnSerializer(BaseSerializer):
72
74
  config = serializers.JSONField(initial={})
75
+ ip = serializers.PrimaryKeyRelatedField(read_only=True)
73
76
  include_shared = True
74
77
 
75
78
  class Meta(BaseMeta):
@@ -83,6 +86,10 @@ class VpnSerializer(BaseSerializer):
83
86
  "ca",
84
87
  "cert",
85
88
  "backend",
89
+ "subnet",
90
+ "ip",
91
+ "webhook_endpoint",
92
+ "auth_token",
86
93
  "notes",
87
94
  "dh",
88
95
  "config",
@@ -90,6 +97,28 @@ class VpnSerializer(BaseSerializer):
90
97
  "modified",
91
98
  ]
92
99
 
100
+ def validate(self, data):
101
+ """Validates the VPN and clears the certificate if CA is changed.
102
+
103
+ When the CA is updated on an existing VPN, the current certificate
104
+ (which was issued by the old CA) becomes invalid. This method clears
105
+ the certificate so the model's ``save()`` method can auto-create a
106
+ new one for the new CA.
107
+ """
108
+ if self.instance and "ca" in data and self.instance.cert:
109
+ # if the CA is changed and new cert not supplied,
110
+ # allow creating a new certificate automatically
111
+ if data["ca"] and self.instance.cert.ca != data["ca"]:
112
+ data["cert"] = None
113
+ return super().validate(data)
114
+
115
+
116
+ class VpnListSerializer(VpnSerializer):
117
+ class Meta(VpnSerializer.Meta):
118
+ fields = VpnSerializer.Meta.fields[:]
119
+ fields.remove("webhook_endpoint")
120
+ fields.remove("auth_token")
121
+
93
122
 
94
123
  class FilterTemplatesByOrganization(serializers.PrimaryKeyRelatedField):
95
124
  def get_queryset(self):
@@ -274,7 +303,7 @@ class DeviceDetailConfigSerializer(BaseConfigSerializer):
274
303
  templates = FilterTemplatesByOrganization(many=True)
275
304
 
276
305
 
277
- class DeviceDetailSerializer(DeviceConfigSerializer):
306
+ class DeviceDetailSerializer(WHOISMixin, DeviceConfigSerializer):
278
307
  config = DeviceDetailConfigSerializer(allow_null=True)
279
308
  is_deactivated = serializers.BooleanField(read_only=True)
280
309