Esempio n. 1
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given webhook targets."""
    encoder = ResourceAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            body = render_custom_content(webhook_target.custom_content,
                                         payload)
        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                if encoding == webhook_target.ENCODING_JSON:
                    adapter = JSONEncoderAdapter(encoder)
                    body = adapter.encode(payload, request=request)
                elif encoding == webhook_target.ENCODING_XML:
                    adapter = XMLEncoderAdapter(encoder)
                    body = adapter.encode(payload, request=request)
                elif encoding == webhook_target.ENCODING_FORM_DATA:
                    adapter = JSONEncoderAdapter(encoder)
                    body = urlencode({
                        'payload':
                        adapter.encode(payload, request=request),
                    })
                else:
                    logging.error(
                        'Unexpected WebHookTarget encoding "%s" for '
                        'ID %s', encoding, webhook_target.pk)
                    continue

                body = body.encode('utf-8')
                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            'X-ReviewBoard-Event': event,
            'Content-Type': webhook_target.encoding,
            'Content-Length': len(body),
            'User-Agent': 'ReviewBoard-WebHook/%s' % get_package_version(),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body)
            headers['X-Hub-Signature'] = 'sha1=%s' % signer.hexdigest()

        logging.info('Dispatching webhook for event %s to %s', event,
                     webhook_target.url)
        urlopen(Request(webhook_target.url, body, headers))
Esempio n. 2
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given WebHook targets."""
    encoder = ResourceAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            try:
                body = render_custom_content(webhook_target.custom_content,
                                             payload)

                body = body.encode('utf-8')
            except Exception as e:
                logging.exception('Could not render WebHook payload: %s', e)
                continue

        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                try:
                    if encoding == webhook_target.ENCODING_JSON:
                        adapter = JSONEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                        body = body.encode('utf-8')
                    elif encoding == webhook_target.ENCODING_XML:
                        adapter = XMLEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                    elif encoding == webhook_target.ENCODING_FORM_DATA:
                        adapter = JSONEncoderAdapter(encoder)
                        body = urlencode({
                            'payload':
                            adapter.encode(payload, request=request),
                        })
                        body = body.encode('utf-8')
                    else:
                        logging.error(
                            'Unexpected WebHookTarget encoding "%s" '
                            'for ID %s', encoding, webhook_target.pk)
                        continue
                except Exception as e:
                    logging.exception('Could not encode WebHook payload: %s',
                                      e)
                    continue

                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            b'X-ReviewBoard-Event':
            event.encode('utf-8'),
            b'Content-Type':
            webhook_target.encoding.encode('utf-8'),
            b'Content-Length':
            len(body),
            b'User-Agent':
            ('ReviewBoard-WebHook/%s' % get_package_version()).encode('utf-8'),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body,
                              hashlib.sha1)
            headers[b'X-Hub-Signature'] = \
                ('sha1=%s' % signer.hexdigest()).encode('utf-8')

        logging.info('Dispatching webhook for event %s to %s', event,
                     webhook_target.url)
        try:
            url = webhook_target.url.encode('utf-8')
            urlopen(Request(url, body, headers))
        except Exception as e:
            logging.exception('Could not dispatch WebHook to %s: %s',
                              webhook_target.url, e)
Esempio n. 3
0
def normalize_webhook_payload(payload, request, use_string_keys=False):
    """Normalize a payload for a WebHook, returning a safe, primitive version.

    This will take a payload containing various data types and model references
    and turn it into a payload built out of specific, whitelisted types
    (strings, bools, ints, dicts, lists, and datetimes). This payload is
    safe to include in custom templates without worrying about access to
    dangerous functions, and is easy to serialize.

    Args:
        payload (dict):
            The payload to normalize.

        request (django.http.HttpRequest):
            The HTTP request from the client.

        use_string_keys (bool, optional):
            Whether to normalize all keys to strings.

    Returns:
        dict:
        The normalized payload.

    Raises:
        TypeError:
            An unsupported data type was found in the payload. This is an
            issue with the caller.
    """
    def _normalize_key(key):
        if key is None:
            if use_string_keys:
                return 'null'

            return None
        elif isinstance(key, six.text_type):
            return key
        elif isinstance(key, (SafeText, bool, float)):
            return six.text_type(key)
        elif isinstance(key, bytes):
            return force_text(key)
        elif isinstance(key, six.integer_types):
            if use_string_keys:
                return force_text(key)

            return key
        else:
            raise TypeError(
                _('%s is not a valid data type for dictionary keys in '
                  'WebHook payloads.') % type(key))

    def _normalize_value(value):
        if value is None:
            return None

        if isinstance(value, SafeText):
            return six.text_type(value)
        elif isinstance(value, bytes):
            return force_text(value)
        elif (isinstance(value, (bool, datetime, float, six.text_type) +
                         six.integer_types)):
            return value
        elif isinstance(value, dict):
            return OrderedDict(
                (_normalize_key(dict_key), _normalize_value(dict_value))
                for dict_key, dict_value in six.iteritems(value))
        elif isinstance(value, (list, tuple)):
            return [_normalize_value(item) for item in value]
        elif isinstance(value, (Model, QuerySet)):
            result = resource_encoder.encode(value, request=request)

            if result is not None:
                return _normalize_value(result)

        raise TypeError(
            _('%s is not a valid data type for values in WebHook payloads.') %
            type(value))

    resource_encoder = ResourceAPIEncoder()

    return _normalize_value(payload)
Esempio n. 4
0
def dispatch_webhook_event(request, webhook_targets, event, payload):
    """Dispatch the given event and payload to the given WebHook targets."""
    encoder = ResourceAPIEncoder()
    bodies = {}

    for webhook_target in webhook_targets:
        if webhook_target.use_custom_content:
            try:
                body = render_custom_content(webhook_target.custom_content,
                                             payload)

                body = body.encode('utf-8')
            except Exception as e:
                logging.exception('Could not render WebHook payload: %s', e)
                continue

        else:
            encoding = webhook_target.encoding

            if encoding not in bodies:
                try:
                    if encoding == webhook_target.ENCODING_JSON:
                        adapter = JSONEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                        body = body.encode('utf-8')
                    elif encoding == webhook_target.ENCODING_XML:
                        adapter = XMLEncoderAdapter(encoder)
                        body = adapter.encode(payload, request=request)
                    elif encoding == webhook_target.ENCODING_FORM_DATA:
                        adapter = JSONEncoderAdapter(encoder)
                        body = urlencode({
                            'payload':
                            adapter.encode(payload, request=request),
                        })
                        body = body.encode('utf-8')
                    else:
                        logging.error(
                            'Unexpected WebHookTarget encoding "%s" '
                            'for ID %s', encoding, webhook_target.pk)
                        continue
                except Exception as e:
                    logging.exception('Could not encode WebHook payload: %s',
                                      e)
                    continue

                bodies[encoding] = body
            else:
                body = bodies[encoding]

        headers = {
            b'X-ReviewBoard-Event':
            event.encode('utf-8'),
            b'Content-Type':
            webhook_target.encoding.encode('utf-8'),
            b'Content-Length':
            len(body),
            b'User-Agent':
            ('ReviewBoard-WebHook/%s' % get_package_version()).encode('utf-8'),
        }

        if webhook_target.secret:
            signer = hmac.new(webhook_target.secret.encode('utf-8'), body,
                              hashlib.sha1)
            headers[b'X-Hub-Signature'] = \
                ('sha1=%s' % signer.hexdigest()).encode('utf-8')

        logging.info('Dispatching webhook for event %s to %s', event,
                     webhook_target.url)
        try:
            url = webhook_target.url
            url_parts = urlsplit(url)

            if url_parts.username or url_parts.password:
                netloc = url_parts.netloc.split('@', 1)[1]
                url = urlunsplit((url_parts.scheme, netloc, url_parts.path,
                                  url_parts.params, url_parts.query))

                password_mgr = HTTPPasswordMgrWithDefaultRealm()
                password_mgr.add_password(None, url, url_parts.username,
                                          url_parts.password)
                handler = HTTPBasicAuthHandler(password_mgr)
                opener = build_opener(handler)
            else:
                opener = build_opener()

            opener.open(Request(url.encode('utf-8'), body, headers))
        except Exception as e:
            logging.exception('Could not dispatch WebHook to %s: %s',
                              webhook_target.url, e)
Esempio n. 5
0
def normalize_webhook_payload(payload, request):
    """Normalize a payload for a WebHook, returning a safe, primitive version.

    This will take a payload containing various data types and model references
    and turn it into a payload built out of specific, whitelisted types
    (strings, bools, ints, dicts, lists, and datetimes). This payload is
    safe to include in custom templates without worrying about access to
    dangerous functions, and is easy to serialize.

    Args:
        payload (dict):
            The payload to normalize.

        request (django.http.HttpRequest):
            The HTTP request from the client.

    Returns:
        dict:
        The normalized payload.

    Raises:
        TypeError:
            An unsupported data type was found in the payload. This is an
            issue with the caller.
    """
    def _normalize_key(key):
        key_type = type(key)

        if key_type in (bool, int, six.text_type, NoneType):
            return key

        raise TypeError(
            _('%s is not a valid data type for dictionary keys in '
              'WebHook payloads.')
            % key_type)

    def _normalize_value(value):
        value_type = type(value)

        if value_type in (bool, datetime, int, six.text_type, NoneType):
            return value
        elif value_type in (SafeText, str):
            return six.text_type(value)
        elif value_type is dict:
            return {
                _normalize_key(dict_key): _normalize_value(dict_value)
                for dict_key, dict_value in six.iteritems(value)
            }
        elif value_type in (list, tuple):
            return [
                _normalize_value(item)
                for item in value
            ]
        elif isinstance(value, (Model, QuerySet)):
            result = resource_encoder.encode(value, request=request)

            if result is not None:
                return _normalize_value(result)

        raise TypeError(
            _('%s is not a valid data type for values in WebHook payloads.')
            % value_type)

    resource_encoder = ResourceAPIEncoder()

    return _normalize_value(payload)