From ec3869c9d00196d049276c86879d917e06d9dde6 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 28 Sep 2013 23:32:15 +0200 Subject: [PATCH] verify username (fqdn) and password (update secret) against the database, improve docstrings --- nsupdate/api/views.py | 52 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/nsupdate/api/views.py b/nsupdate/api/views.py index 8ec8846..535ecb1 100644 --- a/nsupdate/api/views.py +++ b/nsupdate/api/views.py @@ -6,6 +6,7 @@ logger = logging.getLogger(__name__) from django.http import HttpResponse from django.conf import settings from main.forms import * +from main.models import Host import dns.inet from main.dnstools import update, SameIpError @@ -23,14 +24,27 @@ def UpdateIpView(request): return HttpResponse('OK', content_type="text/plain") -def basic_challenge(realm): - response = HttpResponse('Authorization Required', content_type="text/plain") +def basic_challenge(realm, content='Authorization Required'): + """ + Construct a 401 response requesting http basic auth. + + :param realm: realm string (displayed by the browser) + :param content: request body content + :return: HttpResponse object + """ + response = HttpResponse(content, content_type="text/plain") response['WWW-Authenticate'] = 'Basic realm="%s"' % (realm, ) response.status_code = 401 return response def basic_authenticate(auth): + """ + Get username and password from http basic auth string. + + :param auth: http basic auth string + :return: username, password + """ authmeth, auth = auth.split(' ', 1) if authmeth.lower() != 'basic': return @@ -40,7 +54,17 @@ def basic_authenticate(auth): def check_auth(username, password): - return password == 'pass' # FIXME + """ + Check username and password against our database. + + :param username: http basic auth username (== fqdn) + :param password: update password + :return: True if authenticated, False otherwise. + """ + # in our case username == fqdn + hosts = Host.objects.filter(fqdn=username, update_secret=password) + assert len(hosts) < 2 + return bool(hosts) def Response(content): @@ -48,15 +72,33 @@ def Response(content): def NicUpdateView(request): + """ + dyndns2 compatible /nic/update API. + + Example URLs: + + Will request username (fqdn) and password (secret) from user, + for interactive testing / updating: + https://nsupdate.info/nic/update + + You can put it also into the url, so the browser will automatically + send the http basic auth with the request: + https://fqdn:secret@nsupdate.info/nic/update + + If the request does not come from the correct IP, you can give it as + a query parameter, you can also give the hostname (then it won't use + the username from http basic auth as the fqdn: + https://fqdn:secret@nsupdate.info/nic/update?hostname=fqdn&myip=1.2.3.4 + """ hostname = request.GET.get('hostname') auth = request.META.get('HTTP_AUTHORIZATION') if auth is None: logger.warning('%s - received no auth' % (hostname, )) - return basic_challenge("authenticate to update DNS") + return basic_challenge("authenticate to update DNS", 'noauth') username, password = basic_authenticate(auth) if not check_auth(username, password): logger.info('%s - received bad credentials, username: %s' % (hostname, username, )) - return Response('badauth') + return basic_challenge("authenticate to update DNS", 'badauth') if hostname is None: # as we use update_username == hostname, we can fall back to that: hostname = username