Пример #1
0
    def _client_request_received(self, server, msg):
        try:
            request = json.loads(msg[1])
        except json.JSONDecodeError as e:
            # we ignore invalid requests
            log.info('Received invalid JSON request from client: %s (%s)', msg,
                     str(e))
            return

        try:
            reply = self._worker.process_client_message(request)
        except Exception as e:
            reply = json_compact_dump(
                {'error': 'Internal Error: {}'.format(e)}, as_bytes=True)

        # an empty result means we shouldn't send a message back
        if not reply:
            return

        # ensure we have the bytes of a JSON string
        # (workers are permitted to return e.g. True or UTF-8 strings)
        if type(reply) is str:
            reply = reply.encode('utf-8')
        elif type(reply) is not bytes:
            reply = json_compact_dump(reply, as_bytes=True)

        reply_msg = [msg[0], reply]

        log.debug('Sending %s', reply_msg)
        server.send_multipart(reply_msg)
Пример #2
0
def sign_json(json_object, signature_name, signing_key):
    '''
    Sign the JSON object. Stores the signature in json_object['signatures'].

    Args:
        json_object (dict): The JSON object to sign.
        signature_name (str): The name of the signing entity.
        signing_key (syutil.crypto.SigningKey): The key to sign the JSON with.

    Returns:
        The modified, signed JSON object.
    '''

    signatures = json_object.pop('signatures', {})
    unsigned = json_object.pop('unsigned', None)

    message = json_compact_dump(json_object, as_bytes=True)
    signed = signing_key.sign(message)
    signature_base64 = encode_base64(signed.signature)

    key_id = '%s:%s' % (signing_key.alg, signing_key.version)
    signatures.setdefault(signature_name, {})[key_id] = signature_base64

    json_object['signatures'] = signatures
    if unsigned is not None:
        json_object['unsigned'] = unsigned

    return json_object
Пример #3
0
def submit_event_message(socket, sender, tag, data, key):
    '''
    Create a new event message, sign it and send it via the specified socket.
    '''
    if not socket:
        return  # don't send the message if we do not have a valid socket
    msg = create_event_message(sender, tag, data, key)
    socket.send_string(json_compact_dump(msg))
Пример #4
0
    def _emit_event(self, subject, data):
        if not self._event_pub_queue:
            return  # do nothing if event publishing is disabled

        tag = create_message_tag('jobs', subject)
        msg = {'tag': tag,
               'uuid': str(uuid.uuid1()),
               'format': '1.0',
               'time': datetime.now().isoformat(),
               'data': data}
        self._event_pub_queue.put([bytes(tag, 'utf-8'),
                                   bytes(json_compact_dump(msg), 'utf-8')])
Пример #5
0
    def _publish_event(self, event):
        # sign outgoing trusted message with our key
        # anything that is in this queue has already been
        # checked and is trusted
        event = self._sign_message(event)
        new_data = json_compact_dump(event)

        # create message
        msg = [bytes(event['tag'], 'utf-8'),
               bytes(new_data, 'utf-8')]

        # send message
        for socket in self._sockets:
            try:
                socket.send_multipart(msg)
            except Exception as e:
                log.warning('Unable to publish event: {} (data was: {})'.format(str(e), str(msg)))
Пример #6
0
def verify_signed_json(json_object, signature_name, verify_key):
    '''
    Check a signature on a signed JSON object.

    Args:
        json_object (dict): The signed JSON object to check.
        signature_name (str): The name of the signature to check.
        verify_key (syutil.crypto.VerifyKey): The key to verify the signature.

    Raises:
        InvalidSignature: If the signature isn't valid
    '''

    try:
        signatures = json_object['signatures']
    except KeyError:
        raise SignatureVerifyException('No signatures on this object')

    key_id = '%s:%s' % (verify_key.alg, verify_key.version)

    try:
        signature_b64 = signatures[signature_name][key_id]
    except Exception:
        raise SignatureVerifyException('Missing signature for {}, {}'.format(
            signature_name, key_id))

    try:
        signature = decode_base64(signature_b64)
    except Exception:
        raise SignatureVerifyException(
            'Invalid signature base64 for {}, {}'.format(
                signature_name, key_id))

    json_object_copy = dict(json_object)
    del json_object_copy['signatures']
    json_object_copy.pop('unsigned', None)

    message = json_compact_dump(json_object_copy, as_bytes=True)
    try:
        verify_key.verify(message, signature)
    except Exception:
        log.exception('Error verifying signature')
        raise SignatureVerifyException(
            'Unable to verify signature for {}'.format(signature_name))
Пример #7
0
 def _error_reply(self, message):
     return json_compact_dump({'error': message})
Пример #8
0
    def _process_job_request(self, session, req_data):
        '''
        Read job request and return a job matching the request or
        null in case we couldn't find any job.
        '''

        client_name = req_data.get('machine_name')
        client_id = req_data.get('machine_id')
        architectures = req_data.get('architectures', [])

        # update information about this client
        worker = session.query(SparkWorker) \
            .filter(SparkWorker.uuid == client_id).one_or_none()

        # we might have a new machine, so set the ID again to create an empty new worker
        if not worker:
            worker = SparkWorker()

            # this may throw an exception which is caought and sent back to the worker
            # (the worker then has the oportunity to fix its UUID)
            try:
                worker.uuid = uuid.UUID(client_id)
            except TypeError as e:
                return self._error_reply(
                    'Failed to parse client UUID: {}'.format(str(e)))

            worker.name = client_name
            worker.enabled = True

            session.add(worker)

        worker.last_ping = datetime.utcnow()

        accepted_kinds = req_data.get('accepts', [])
        if type(accepted_kinds) is list:
            worker.accepts = accepted_kinds
        else:
            worker.accepts = [str(accepted_kinds)]
        session.commit()

        job_data = None
        job_assigned = False
        for accepted_kind in worker.accepts:
            for arch_name in architectures:
                job = None
                if arch_name == self._arch_indep_affinity:
                    # we can  maybe assign an arch:all job to this machine
                    job = self._assign_suitable_job(session, accepted_kind,
                                                    'all', worker.uuid)

                # use the first job with a matching architecture/kind if we didn't find an arch:all job previously
                if not job:
                    job = self._assign_suitable_job(session, accepted_kind,
                                                    arch_name, worker.uuid)

                if job:
                    job_data = self._get_job_details(session, job)
                    job_assigned = True

                    event_data = {
                        'job_id': job_data['uuid'],
                        'client_name': client_name,
                        'client_id': client_id,
                        'job_module': job_data['module'],
                        'job_kind': job_data['kind'],
                        'job_version': job_data['version'],
                        'job_architecture': job_data['architecture']
                    }
                    self._emit_event('job-assigned', event_data)
                    break
            if job_assigned:
                break

        return json_compact_dump(job_data)