diff --git a/functions/slack/main.py b/functions/slack/main.py index 3da846517c05..47f3ef037aff 100644 --- a/functions/slack/main.py +++ b/functions/slack/main.py @@ -12,12 +12,11 @@ # limitations under the License. # [START functions_slack_setup] -import hashlib -import hmac import os from flask import jsonify import googleapiclient.discovery +from slack.signature import SignatureVerifier kgsearch = googleapiclient.discovery.build( @@ -29,19 +28,12 @@ # [START functions_verify_webhook] -# Python 3+ version of https://github.com/slackapi/python-slack-events-api/blob/master/slackeventsapi/server.py def verify_signature(request): - timestamp = request.headers.get('X-Slack-Request-Timestamp', '') - signature = request.headers.get('X-Slack-Signature', '') + request.get_data() # Decodes received requests into request.data - req = str.encode('v0:{}:'.format(timestamp)) + request.get_data() - request_digest = hmac.new( - str.encode(os.environ['SLACK_SECRET']), - req, hashlib.sha256 - ).hexdigest() - request_hash = 'v0={}'.format(request_digest) + verifier = SignatureVerifier(os.environ['SLACK_SECRET']) - if not hmac.compare_digest(request_hash, signature): + if not verifier.is_valid_request(request.data, request.headers): raise ValueError('Invalid request/credentials.') # [END functions_verify_webhook] diff --git a/functions/slack/main_test.py b/functions/slack/main_test.py index a9f471d0e556..7767498dbf37 100644 --- a/functions/slack/main_test.py +++ b/functions/slack/main_test.py @@ -13,10 +13,12 @@ import json import os +import time import googleapiclient.discovery import mock import pytest +from slack.signature import SignatureVerifier import main @@ -28,8 +30,9 @@ class Request(object): - def __init__(self, data=b''): + def __init__(self, data='', headers={}): self.data = data + self.headers = headers def get_data(self): return self.data @@ -39,19 +42,28 @@ class TestGCFPySlackSample(object): def test_verify_signature_request_form_empty(self): with pytest.raises(ValueError): request = Request() - request.headers = {} main.verify_signature(request) def test_verify_signature_token_incorrect(self): with pytest.raises(ValueError): - request = Request() - request.headers = {'X-Slack-Signature': '12345'} + request = Request(headers={'X-Slack-Signature': '12345'}) main.verify_signature(request) def test_verify_web_hook_valid_request(self): request = Request() + request.body = '' + + now = str(int(time.time())) + + verifier = SignatureVerifier(os.environ['SLACK_SECRET']) + test_signature = verifier.generate_signature( + timestamp=now, + body='' + ) + request.headers = { - 'X-Slack-Signature': os.environ['SLACK_TEST_SIGNATURE'] + 'X-Slack-Request-Timestamp': now, + 'X-Slack-Signature': test_signature } main.verify_signature(request) @@ -77,14 +89,25 @@ def test_kg_search(self): entities = main.kgsearch.entities.return_value search = entities.search.return_value search.execute.return_value = example_response + request = Request() - request.method = 'POST' - request.headers = { - 'X-Slack-Signature': os.environ['SLACK_TEST_SIGNATURE'] - } request.form = { 'text': 'lion' } + request.data = json.dumps(request.form) + + now = str(int(time.time())) + verifier = SignatureVerifier(os.environ['SLACK_SECRET']) + test_signature = verifier.generate_signature( + timestamp=now, + body=request.data + ) + + request.method = 'POST' + request.headers = { + 'X-Slack-Request-Timestamp': now, + 'X-Slack-Signature': test_signature + } with mock.patch('main.jsonify', side_effect=json.dumps): response = main.kg_search(request) diff --git a/functions/slack/requirements.txt b/functions/slack/requirements.txt index 22c9a3317f2a..b3d3e22355f1 100644 --- a/functions/slack/requirements.txt +++ b/functions/slack/requirements.txt @@ -1,3 +1,3 @@ google-api-python-client==1.10.0 flask==1.1.2 -slackeventsapi==2.2.0 +slackclient==2.7.3