CUP2Middleware.is_cup2_request()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
import logging
2
from hashlib import sha256
3
4
from django.conf import settings
5
from django.http import HttpResponse
6
from django.utils import timezone
7
8
import pytz
9
from omaha.dynamic_preferences_registry import global_preferences_manager as gpm
10
from ecdsa import SigningKey
11
from ecdsa.util import sigencode_der
12
13
logger = logging.getLogger(__name__)
14
15
16
class CUP2Exception(Exception):
17
    pass
18
19
20
class TimezoneMiddleware(object):
21
    def process_request(self, request):
22
        tzname = gpm['Timezone__timezone']
23
        if tzname:
24
            timezone.activate(pytz.timezone(tzname))
25
        else:
26
            timezone.deactivate()
27
28
29
class CUP2Middleware(object):
30
    """Support CUP2 protocol of Omaha Client.
31
    """
32
33
    def __init__(self):
34
        self.sk = {}
35
        # Loading signature keys to memory
36
        for keyid, private_key in settings.CUP_PEM_KEYS.iteritems():
37
            self.sk[keyid] = SigningKey.from_pem(open(private_key).read())
38
39
    def process_request(self, request):
40
        if getattr(settings, 'CUP_REQUEST_VALIDATION', False) and self.is_cup2_request(request):
41
            try:
42
                self.validate_cup2_request(request)
43
            except Exception as e:
44
                logger.error('%s: %s\nrequest:\n%s\n\n%s\n' % (e.__class__.__name__, e.message,
45
                                                               request.META, request.body))
46
                msg = b'<?xml version="1.0" encoding="utf-8"?><data><message>Bad Request</message></data>'
47
                return HttpResponse(msg, status=400, content_type="text/html; charset=utf-8")
48
49
    def process_response(self, request, response):
50
        if getattr(settings, 'CUP_REQUEST_VALIDATION', False) and  self.is_cup2_request(request) and response.status_code // 100 == 2:
51
            self.sign_cup2_response(request, response)
52
53
        return response
54
55
    @staticmethod
56
    def is_cup2_request(request):
57
        """Detects CUP2 request by passed cup2key parameter.
58
        """
59
        return request.GET.get('cup2key') is not None
60
61
    def validate_cup2_request(self, request):
62
        cup2key = request.GET.get('cup2key')
63
        cup2hreq = request.GET.get('cup2hreq')
64
65
        keyid, k = cup2key.split(':')
66
        if keyid not in self.sk.keys():
67
            raise CUP2Exception('There is no key with id %s' % keyid)
68
69
        request_hash = sha256(request.body).hexdigest()
70
        if cup2hreq and request_hash != cup2hreq:
71
            raise CUP2Exception('Bad request hash\n"%s" != "%s"' % (request_hash, cup2hreq))
72
73
    def sign_cup2_response(self, request, response):
74
        cup2key = request.GET.get('cup2key')
75
76
        request_hash = sha256(request.body).digest()
77
        response_hash = sha256(response.content).digest()
78
79
        keyid, k = cup2key.split(':')
80
        # hash( hash(request) | hash(response) | cup2key )
81
        message = sha256(request_hash + response_hash + cup2key.encode()).digest()
82
        signature = self.sk[keyid].sign(message, hashfunc=sha256, sigencode=sigencode_der, k=int(k))
83
84
        response['ETag'] = '%s:%s' % (signature.encode('hex'), request_hash.encode('hex'))
85
86
87
class LoggingMiddleware(object):
88
    def process_request(self, request):
89
        if 'live' in request.path:
90
            logging.info('process_request')
91
92
    def process_view(self, request, view_func, view_args, view_kwargs):
93
        if 'live' in request.path:
94
            logging.info('process_view')
95
96
    def process_response(self, request, response):
97
        if 'live' in request.path:
98
            logging.info('process_response')
99
        return response
100