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))
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)
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)
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)
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)