"""Transifex webhook handler """ import base64 import hashlib import hmac import json import os import requests import time import logging from subprocess import call from base64 import b64encode from buildbot.process.properties import Properties from buildbot.util import bytes2unicode, unicode2bytes from buildbot.www.hooks.base import BaseHookHandler from dateutil.parser import parse as dateparse from twisted.internet import defer from twisted.python import log _HEADER_USER_AGENT = 'User-Agent' _HEADER_SIGNATURE = 'X-TX-Signature' _HEADER_URL_PATH = 'X-TX-Url' _HTTP_DATE = 'date' _EVENT_KEY = 'event' class TransifexHandler(BaseHookHandler): def __init__(self, master, secret, transifex_to_github_map, options=None): if not options: options = {} self.secret = secret self.master = master self.options = options self.transifex_to_github_map = transifex_to_github_map def returnMessage(self, status = False, message = "Unimplemented"): output = json.dumps({"status": "OK" if status else "FAIL", "message": message}) return [output, [('Content-type', 'application/json')]] def _verifyTransifexSignature( self, request, content, signature, header_signature ): http_verb = 'POST' http_url_path = request.getHeader(_HEADER_URL_PATH) http_gmt_date = request.getHeader(_HTTP_DATE) content_md5 = hashlib.md5(content).hexdigest() msg = b'\n'.join([ http_verb, http_url_path, http_gmt_date, content_md5 ]) tx_signature = base64.b64encode( hmac.new( key=self.rendered_secret, msg=msg, digestmod=hashlib.sha256 ).digest() ) if tx_signature != header_signature: return False if signature != request.getHeader(_HEADER_SIGNATURE): return False return True def process_translation_completed(self, payload, codebase): changes = [] transifex_response = self._transform_variables(payload['project'], payload['resource']) # if 'pybitmessage-test' in transifex_response['project'] and 'messagespot' in transifex_response['resource']: # if 'translation_completed' in transifex_response['event']: change = { 'author': "buildbot-transifex", 'resource': transifex_response[resource], 'branch': transifex_response["branch"], 'project': transifex_response["project"], 'properties': { "transifex_language": payload.get("language", "None"), "transifex_event": payload.get("event", "None"), "transifex_project": payload.get("project", "None"), "transifex_resource": payload.get("resource", "None") } } if codebase is not None: change['codebase'] = codebase changes.insert(0, change) return changes def _transform_variables(self, transifex_project, transifex_resource): key = "{}/{}".format(transifex_project, transifex_resource) _map = self.map[key] repository = _map["repository"] project = transifex_project return { 'project': project, 'repository': repository, 'branch': _map["branch"], } @defer.inlineCallbacks def getChanges(self, request): change = {} self.secret = None if isinstance(self.options, dict): self.secret = self.options.get('secret') try: content = request.content.read() content_text = bytes2unicode(content) payload = json.loads(content_text) except Exception as exception: raise ValueError('Error loading JSON: ' + str(exception)) if self.secret is not None: p = Properties() p.master = self.master option = self.options self.rendered_secret = yield p.render(self.secret) signature = hmac.new( unicode2bytes(self.rendered_secret), unicode2bytes(content_text.strip()), digestmod=hashlib.sha256) header_signature = bytes2unicode( request.getHeader(_HEADER_SIGNATURE)) if not self._verifyTransifexSignature(request, content, self.rendered_secret, signature, header_signature): logging.warning("Verify Transifex Signature fail.") return False else: logging.warning("Verify Transifex Signature ok") event_type = payload.get("event", "None") change["properties"] = { "transifex_language": payload.get("language", "None"), "transifex_event": payload.get("event", "None"), "transifex_project": payload.get("project", "None"), "transifex_resource": payload.get("resource", "None") } transiform_map = self._transform_variables(payload['project'], payload['resource']) change["changes"] = { "author": "buildbot-transifex", "repository": transiform_map['repository'], "project": transiform_map['project'], "branch": transiform_map["branch"] } log.msg("Received event '{}' from transifex".format(event_type)) codebase = "" changes = [] handler_function = getattr(self, 'process_{}'.format(event_type), None) if not handler_function: log.msg("Ignoring transifex event '{}'".format(event_type)) else: changes = handler_function(payload, event_type, codebase) return (changes, 'transifex') transifex = TransifexHandler(transifex_to_github_map)