예제 #1
0
    def test_key_by_id(self):
        with self.settings(
                RESTCLIENTS_KWS_DAO_CLASS='restclients.dao_implementation.kws.File'):

            kws = KWS()
            key = kws.get_key('ee99defd-baee-43b0-9e1e-f8238dd106bb')
            self.assertEquals(key.algorithm, 'AES128CBC', 'Correct algorithm')
            self.assertEquals(key.cipher_mode, 'CBC', 'Correct cipher mode')
            self.assertEquals(key.expiration.isoformat(), '2013-04-11T13:44:33', 'Correct expiration')
            self.assertEquals(key.key_id, 'ee99defd-baee-43b0-9e1e-f8238dd106bb', 'Correct key ID')
            self.assertEquals(key.key, 'Uv2JsxggfxF9OQNzIxAzDQ==', 'Correct key')
            self.assertEquals(key.size, 128, 'Correct key size')
            self.assertEquals(key.url, 'https://it-wseval1.s.uw.edu/key/v1/encryption/ee99defd-baee-43b0-9e1e-f8238dd106bb.json', 'Correct key URL')
예제 #2
0
    def __init__(self, settings, message):
        """
        UW Course Event object

        Takes a dict representing a UW Course Event Message

        Raises EventException
        """
        self._kws = KWS()
        self._settings = settings
        self._re_guid = re.compile(
            r'^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$', re.I)
        self._re_json_cruft = re.compile(r'[^{]*({.*})[^}]*')

        try:
            self._header = message['Header']
            self._body = message['Body']
        except KeyError:
            self._header = {}
            self._body = message

        if ('MessageType' in self._header and
                self._header['MessageType'] != self._eventMessageType):
            raise EventException(
                'Unknown Message Type: %s' % (self._header['MessageType']))

        self._log = getLogger(__name__)
예제 #3
0
 def test_key_by_id(self):
     kws = KWS()
     key = kws.get_key('ee99defd-baee-43b0-9e1e-f8238dd106bb')
     self.assertEquals(key.algorithm, 'AES128CBC', 'Correct algorithm')
     self.assertEquals(key.cipher_mode, 'CBC', 'Correct cipher mode')
     self.assertEquals(
         key.expiration.isoformat(),
         '2013-04-11T13:44:33', 'Correct expiration')
     self.assertEquals(
         key.key_id,
         'ee99defd-baee-43b0-9e1e-f8238dd106bb', 'Correct key ID')
     self.assertEquals(key.key, 'Uv2JsxggfxF9OQNzIxAzDQ==', 'Correct key')
     self.assertEquals(key.size, 128, 'Correct key size')
     self.assertEquals(
         key.url,
         'https://it-wseval1.s.uw.edu/key/v1/encryption/ee99defd-baee-43b0-9e1e-f8238dd106bb.json',
         'Correct key URL')
예제 #4
0
class EventBase(object):
    """
    UW Course Event Handler
    """

    _header = None
    _body = None

    def __init__(self, settings, message):
        """
        UW Course Event object

        Takes a dict representing a UW Course Event Message

        Raises EventException
        """
        self._kws = KWS()
        self._settings = settings
        self._re_guid = re.compile(
            r'^[\da-f]{8}(-[\da-f]{4}){3}-[\da-f]{12}$', re.I)
        self._re_json_cruft = re.compile(r'[^{]*({.*})[^}]*')

        try:
            self._header = message['Header']
            self._body = message['Body']
        except KeyError:
            self._header = {}
            self._body = message

        if ('MessageType' in self._header and
                self._header['MessageType'] != self._eventMessageType):
            raise EventException(
                'Unknown Message Type: %s' % (self._header['MessageType']))

        self._log = getLogger(__name__)

    def validate(self):
        try:
            t = self._header['Version']
            if t != self._eventMessageVersion:
                raise EventException('Unknown Version: ' + t)

            to_sign = self._header['MessageType'] + '\n' \
                + self._header['MessageId'] + '\n' \
                + self._header['TimeStamp'] + '\n' \
                + self._body + '\n'

            sig_conf = {
                'cert': {
                    'type': 'url',
                    'reference': self._header['SigningCertURL']
                }
            }

            Signature(sig_conf).validate(to_sign.encode('ascii'),
                                         b64decode(self._header['Signature']))
        except KeyError as err:
            if len(self._header):
                raise EventException('Invalid Signature Header: %s' % (err))
        except CryptoException as err:
            raise EventException('Crypto: %s' % (err))
        except Exception as err:
            raise EventException('Invalid signature: %s' % (err))

    def extract(self):
        try:
            if 'Encoding' not in self._header:
                if isinstance(self._body, basestring):
                    return(json.loads(
                        self._re_json_cruft.sub(r'\g<1>', self._body)))
                elif isinstance(self._body, dict):
                    return self._body
                else:
                    raise EventException('No body encoding')

            t = self._header['Encoding']
            if str(t).lower() != 'base64':
                raise EventException('Unkown encoding: ' + t)

            t = self._header.get('Algorithm', 'aes128cbc')
            if str(t).lower() != 'aes128cbc':
                raise EventException('Unsupported algorithm: ' + t)

            key = None
            if 'KeyURL' in self._header:
                key = self._kws._key_from_json(
                    self._kws._get_resource(self._header['KeyURL']))
            elif 'KeyId' in self._header:
                key = self._kws.get_key(self._header['KeyId'])
            else:
                try:
                    key = self._kws.get_current_key(
                        self._header['MessageType'])
                    if not re.match(r'^\s*{.+}\s*$', self._body):
                        raise CryptoException()
                except (ValueError, CryptoException) as err:
                    RestClientsCache().delete_cached_kws_current_key(
                        self._header['MessageType'])
                    key = self._kws.get_current_key(
                        self._header['MessageType'])

            cipher = aes128cbc(b64decode(key.key),
                               b64decode(self._header['IV']))
            body = cipher.decrypt(b64decode(self._body))
            return(json.loads(self._re_json_cruft.sub(r'\g<1>', body)))
        except KeyError as err:
            self._log.error(
                "Key Error: %s\nHEADER: %s" % (err, self._header))
            raise
        except (ValueError, CryptoException) as err:
            raise EventException('Cannot decrypt: %s' % (err))
        except DataFailureException as err:
            msg = "Request failure for %s: %s (%s)" % (
                err.url, err.msg, err.status)
            self._log.error(msg)
            raise EventException(msg)
        except Exception as err:
            raise EventException('Cannot read: %s' % (err))

    def process(self):
        if self._settings.get('VALIDATE_MSG_SIGNATURE', True):
            self.validate()

        self.process_events(self.extract())

    def process_events(self, events):
        raise EventException('No event processor defined')

    def load_enrollments(self, enrollments):
        enrollment_count = len(enrollments)
        if enrollment_count:
            for enrollment in enrollments:
                try:
                    Enrollment.objects.add_enrollment(enrollment)
                except Exception as err:
                    raise EventException('Load enrollment failed: %s' % (err))

            try:
                self.record_success(enrollment_count)
            except:
                pass

    def record_success_to_log(self, log_model, event_count):
        minute = int(floor(time() / 60))
        try:
            e = log_model.objects.get(minute=minute)
            e.event_count += event_count
        except log_model.DoesNotExist:
            e = log_model(minute=minute, event_count=event_count)

        e.save()

        if e.event_count <= 5:
            limit = self._settings.get(
                'EVENT_COUNT_PRUNE_AFTER_DAY', 7) * 24 * 60
            prune = minute - limit
            log_model.objects.filter(minute__lt=prune).delete()