Commit 7955cd21 authored by Miquel Torres's avatar Miquel Torres Committed by GitHub
Browse files

Merge pull request #214 from mwatts15/34-add-security-measures-to-posting

Added http basic authentication to results resources
parents ad9ede13 d0483f4e
default_app_config = 'codespeed.apps.CodespeedConfig'
from django.apps import AppConfig
from django.conf import settings
class CodespeedConfig(AppConfig):
name = 'codespeed'
def ready(self):
import warnings
if settings.ALLOW_ANONYMOUS_POST:
warnings.warn("Results can be posted by unregistered users")
warnings.warn(
"In the future anonymous posting will be disabled by default",
category=FutureWarning)
elif not settings.REQUIRE_SECURE_AUTH:
warnings.warn(
"REQUIRE_SECURE_AUTH is not True. This server may prompt for"
" user credentials to be submitted in plaintext")
import logging
from functools import wraps
from django.contrib.auth import authenticate, login
from django.http import HttpResponse, HttpResponseForbidden
from django.conf import settings
from base64 import b64decode
__ALL__ = ['basic_auth_required']
logger = logging.getLogger(__name__)
def basic_auth_required(realm='default'):
def _helper(func):
@wraps(func)
def _decorator(request, *args, **kwargs):
allowed = False
logger.info('request is secure? {}'.format(request.is_secure()))
if settings.ALLOW_ANONYMOUS_POST:
logger.debug('allowing anonymous post')
allowed = True
elif hasattr(request, 'user') and request.user.is_authenticated():
allowed = True
elif 'HTTP_AUTHORIZATION' in request.META:
logger.debug('checking for http authorization header')
if settings.REQUIRE_SECURE_AUTH and not request.is_secure():
return insecure_connection_response()
http_auth = request.META['HTTP_AUTHORIZATION']
authmeth, auth = http_auth.split(' ', 1)
if authmeth.lower() == 'basic':
username, password = decode_basic_auth(auth)
user = authenticate(username=username, password=password)
if user is not None and user.is_active:
logger.info(
'Authentication succeeded for {}'.format(username))
login(request, user)
allowed = True
else:
logger.info(
'Failed auth for {}'.format(username))
return HttpResponseForbidden()
if allowed:
return func(request, *args, **kwargs)
if settings.REQUIRE_SECURE_AUTH and not request.is_secure():
logger.debug('not requesting auth over an insecure channel')
return insecure_connection_response()
else:
res = HttpResponse()
res.status_code = 401
res.reason_phrase = 'Unauthorized'
res['WWW-Authenticate'] = 'Basic realm="{}"'.format(realm)
return res
return _decorator
return _helper
def insecure_connection_response():
return HttpResponseForbidden('Secure connection required')
def decode_basic_auth(auth):
authb = b64decode(auth.strip())
auth = authb.decode()
return auth.split(':', 1)
......@@ -68,3 +68,7 @@ COMP_EXECUTABLES = None # Which executable + revision should be checked as defa
# ('myexe', 'L'),]
USE_MEDIAN_BANDS = True # True to enable median bands on Timeline view
ALLOW_ANONYMOUS_POST = True # Whether anonymous users can post results
REQUIRE_SECURE_AUTH = True # Whether auth needs to be over a secure channel
......@@ -3,13 +3,14 @@ from datetime import datetime, timedelta
import copy
import json
from django.test import TestCase
from django.test import TestCase, override_settings
from django.core.urlresolvers import reverse
from codespeed.models import (Project, Benchmark, Revision, Branch, Executable,
Environment, Result, Report)
@override_settings(ALLOW_ANONYMOUS_POST=True)
class TestAddResult(TestCase):
def setUp(self):
......@@ -162,6 +163,7 @@ class TestAddResult(TestCase):
response.content.decode(), "Result data saved successfully")
@override_settings(ALLOW_ANONYMOUS_POST=True)
class TestAddJSONResults(TestCase):
def setUp(self):
......@@ -361,6 +363,7 @@ class TestTimeline(TestCase):
[u'2011/04/13 17:04:22 ', 2000.0, 1.11111, u'2', u'', u'default'])
@override_settings(ALLOW_ANONYMOUS_POST=True)
class TestReports(TestCase):
def setUp(self):
......
......@@ -14,6 +14,7 @@ from django.views.decorators.http import require_GET, require_POST
from django.views.decorators.csrf import csrf_exempt
from django.template import RequestContext
from django.conf import settings
from .auth import basic_auth_required
from .models import (Environment, Report, Project, Revision, Result,
Executable, Benchmark, Branch)
......@@ -697,6 +698,7 @@ def displaylogs(request):
@csrf_exempt
@require_POST
@basic_auth_required('results')
def add_result(request):
response, error = save_result(request.POST)
if error:
......@@ -710,6 +712,7 @@ def add_result(request):
@csrf_exempt
@require_POST
@basic_auth_required('results')
def add_json_results(request):
if not request.POST.get('json'):
return HttpResponseBadRequest("No key 'json' in POST payload")
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment