Skip to content
This repository has been archived by the owner on Jul 9, 2020. It is now read-only.

Commit

Permalink
Added consistent registration feature
Browse files Browse the repository at this point in the history
  • Loading branch information
nemesifier committed May 13, 2016
1 parent 9d0a09c commit 6f22de8
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 9 deletions.
18 changes: 18 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,24 @@ This feature is enabled by default.
Autoregistration must be supported on the devices in order to work, see `openwisp-config automatic
registration <https://github.com/openwisp/openwisp-config#automatic-registration>`_ for more information.

``NETJSONCONFIG_CONSISTENT_REGISTRATION``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+--------------+-------------+
| **type**: | ``bool`` |
+--------------+-------------+
| **default**: | ``True`` |
+--------------+-------------+

Whether devices that are already registered are recognized when reflashed or reset, hence keeping
the existing configuration without creating a new one.

This feature is enabled by default.

Autoregistration must be enabled also on the devices in order to work, see `openwisp-config
consistent key generation <https://github.com/openwisp/openwisp-config#consistent-key-generation>`_
for more information.

``NETJSONCONFIG_SHARED_SECRET``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
40 changes: 31 additions & 9 deletions django_netjsonconfig/controller/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods

from .. import settings
from ..models import Config
from ..settings import BACKENDS, REGISTRATION_ENABLED, SHARED_SECRET
from ..utils import (ControllerResponse, forbid_unallowed, get_config_or_404,
send_config, update_last_ip)

Expand Down Expand Up @@ -52,30 +52,52 @@ def report_status(request, pk):
content_type='text/plain')


if REGISTRATION_ENABLED:
if settings.REGISTRATION_ENABLED:
@csrf_exempt
@require_http_methods(['POST'])
def register(request):
"""
registers new config
"""
# ensure request is well formed and authorized
allowed_backends = [path for path, name in BACKENDS]
required_params = [('secret', SHARED_SECRET),
allowed_backends = [path for path, name in settings.BACKENDS]
required_params = [('secret', settings.SHARED_SECRET),
('name', None),
('backend', allowed_backends)]
# valid required params or forbid
for key, value in required_params:
bad_response = forbid_unallowed(request, 'POST', key, value)
if bad_response:
return bad_response
# create new Config
config = Config.objects.create(name=request.POST.get('name'),
backend=request.POST.get('backend'),
last_ip=request.META.get('REMOTE_ADDR'))
key = None
last_ip = request.META.get('REMOTE_ADDR')
if settings.CONSISTENT_REGISTRATION:
key = request.POST.get('key')
# try retrieving existing Config first
# (key is filled only if CONSISTENT_REGISTRATION is enabled)
try:
config = Config.objects.get(key=key)
# create new Config otherwise
except Config.DoesNotExist:
new = True
options = dict(name=request.POST.get('name'),
backend=request.POST.get('backend'),
last_ip=last_ip)
# do not specify key if ``None``, would cause exception
if key:
options['key'] = key
config = Config.objects.create(**options)
# update last_ip on existing configs
else:
new = False
config.last_ip = last_ip
config.save()
# return id and key in response
s = 'registration-result: success\n' \
'uuid: {id}\n' \
'key: {key}\n'
'key: {key}\n' \
'hostname: {name}\n'
s += 'is-new: %s\n' % (int(new))
return ControllerResponse(s.format(**config.__dict__),
content_type='text/plain',
status=201)
1 change: 1 addition & 0 deletions django_netjsonconfig/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

BACKENDS = DEFAULT_BACKENDS + getattr(settings, 'NETJSONCONFIG_BACKENDS', [])
REGISTRATION_ENABLED = getattr(settings, 'NETJSONCONFIG_REGISTRATION_ENABLED', True)
CONSISTENT_REGISTRATION = getattr(settings, 'NETJSONCONFIG_CONSISTENT_REGISTRATION', True)
SHARED_SECRET = getattr(settings, 'NETJSONCONFIG_SHARED_SECRET', '')
CONTEXT = getattr(settings, 'NETJSONCONFIG_CONTEXT', {})
DEFAULT_BACKEND = getattr(settings, 'NETJSONCONFIG_DEFAULT_BACKEND', DEFAULT_BACKENDS[0][0])
82 changes: 82 additions & 0 deletions django_netjsonconfig/tests/test_controller.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
from hashlib import md5

from django.conf import settings
from django.core.urlresolvers import reverse
from django.test import TestCase

from django_netjsonconfig import settings as app_settings
from django_netjsonconfig.models import Config

TEST_MACADDR = '00:11:22:33:44:55'
mac_plus_secret = '%s+%s' % (TEST_MACADDR, settings.NETJSONCONFIG_SHARED_SECRET)
TEST_CONSISTENT_KEY = md5(mac_plus_secret.encode()).hexdigest()


class TestController(TestCase):
"""
Expand Down Expand Up @@ -143,6 +149,48 @@ def test_register_405(self):
response = self.client.get(self.register_url)
self.assertEqual(response.status_code, 405)

def test_consistent_registration_new(self):
response = self.client.post(self.register_url, {
'secret': settings.NETJSONCONFIG_SHARED_SECRET,
'name': TEST_MACADDR,
'key': TEST_CONSISTENT_KEY,
'backend': 'netjsonconfig.OpenWrt'
})
self.assertEqual(response.status_code, 201)
lines = response.content.decode().split('\n')
self.assertEqual(lines[0], 'registration-result: success')
uuid = lines[1].replace('uuid: ', '')
key = lines[2].replace('key: ', '')
new = lines[4].replace('is-new: ', '')
self.assertEqual(new, '1')
self.assertEqual(key, TEST_CONSISTENT_KEY)
c = Config.objects.get(pk=uuid, key=TEST_CONSISTENT_KEY)
self._check_header(response)
self.assertIsNotNone(c.last_ip)

def test_consistent_registration_existing(self):
c = self._create_config()
c.key = TEST_CONSISTENT_KEY
c.save()
response = self.client.post(self.register_url, {
'secret': settings.NETJSONCONFIG_SHARED_SECRET,
'name': TEST_MACADDR,
'key': TEST_CONSISTENT_KEY,
'backend': 'netjsonconfig.OpenWrt'
})
self.assertEqual(response.status_code, 201)
lines = response.content.decode().split('\n')
self.assertEqual(lines[0], 'registration-result: success')
uuid = lines[1].replace('uuid: ', '')
key = lines[2].replace('key: ', '')
hostname = lines[3].replace('hostname: ', '')
new = lines[4].replace('is-new: ', '')
self.assertEqual(hostname, c.name)
self.assertEqual(new, '0')
c2 = Config.objects.get(pk=uuid, key=key)
self.assertEqual(c2.id, c.id)
self.assertEqual(c2.key, c.key)

def test_report_status_running(self):
c = self._create_config()
response = self.client.post(reverse('controller:report_status', args=[c.pk]),
Expand Down Expand Up @@ -194,3 +242,37 @@ def test_report_status_405(self):
response = self.client.get(reverse('controller:report_status', args=[c.pk]),
{'key': c.key, 'status': 'running'})
self.assertEqual(response.status_code, 405)


class TestConsistentRegistrationDisabled(TestCase):
"""
tests for django_netjsonconfig.controller
"""
register_url = reverse('controller:register')

@classmethod
def setUpClass(cls):
super(TestConsistentRegistrationDisabled, cls).setUpClass()
app_settings.CONSISTENT_REGISTRATION = False

@classmethod
def tearDownClass(cls):
super(TestConsistentRegistrationDisabled, cls).tearDownClass()
app_settings.CONSISTENT_REGISTRATION = True

def test_consistent_registration_disabled(self):
response = self.client.post(self.register_url, {
'secret': settings.NETJSONCONFIG_SHARED_SECRET,
'name': TEST_MACADDR,
'key': TEST_CONSISTENT_KEY,
'backend': 'netjsonconfig.OpenWrt'
})
self.assertEqual(response.status_code, 201)
lines = response.content.decode().split('\n')
self.assertEqual(lines[0], 'registration-result: success')
key = lines[2].replace('key: ', '')
new = lines[4].replace('is-new: ', '')
self.assertEqual(new, '1')
self.assertNotEqual(key, TEST_CONSISTENT_KEY)
self.assertEqual(Config.objects.filter(key=TEST_CONSISTENT_KEY).count(), 0)
self.assertEqual(Config.objects.filter(key=key).count(), 1)

0 comments on commit 6f22de8

Please sign in to comment.