X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=turbot%2Fslack.py;h=6ea9166595e7b236eeeaa009d41fdd0653e1939d;hb=HEAD;hp=34fe44d0a6cf543ff89a02763747d358b5cd9047;hpb=3498d36fd61689e078c6eb46f1b103272f524e36;p=turbot diff --git a/turbot/slack.py b/turbot/slack.py index 34fe44d..6ea9166 100644 --- a/turbot/slack.py +++ b/turbot/slack.py @@ -1,62 +1,67 @@ -from flask import current_app -from slack import WebClient -from slack.errors import SlackApiError -from slack.signature import SignatureVerifier +import hashlib +import hmac import os -import requests +import boto3 -slack_signing_secret = os.environ['SLACK_SIGNING_SECRET'] -slack_bot_token = os.environ['SLACK_BOT_TOKEN'] +if 'SLACK_SIGNING_SECRET' in os.environ: + slack_signing_secret = os.environ['SLACK_SIGNING_SECRET'] +else: + ssm = boto3.client('ssm') + response = ssm.get_parameter(Name='SLACK_SIGNING_SECRET', + WithDecryption=True) + slack_signing_secret = response['Parameter']['Value'] + os.environ['SLACK_SIGNING_SECRET'] = slack_signing_secret -signature_verifier = SignatureVerifier(slack_signing_secret) -slack_client = WebClient(slack_bot_token) +slack_signing_secret = bytes(os.environ['SLACK_SIGNING_SECRET'], 'utf-8') -def slack_is_valid_request(request): - """Returns true if request actually came from Slack. +def slack_is_valid_request(slack_signature, timestamp, body): + """Returns True if the timestamp and body correspond to signature. - By means of checking the requests signature together with the slack - signing key. + This implements the Slack signature verification using the slack + signing secret (obtained via an SSM parameter in code above).""" - Note: If flask is in debug mode, this function will always return true.""" + content = "v0:{}:{}".format(timestamp, body).encode('utf-8') - if current_app.debug: - return True - - data = request.get_data() - headers = request.headers - - return signature_verifier.is_valid_request(data, headers) - -def slack_send_reply(request, text): - """Send a Slack message as a reply to a specified request. - - If the request is associated with a direct message, the reply is - made by using the "response_url" from the request. Otherwise, the - reply will be sent to the channel associated with the request. - - Note: If flask is in debug mode, this function will just print the - text to stdout.""" - - app = current_app - channel_name = request.form.get('channel_name') - response_url = request.form.get('response_url') - channel = request.form.get('channel_id') + signature = 'v0=' + hmac.new(slack_signing_secret, + content, + hashlib.sha256).hexdigest() - if (app.debug): - print("Sending message to channel '{}': {}".format(channel, text)) - return - - if (channel_name == "directmessage"): - resp = requests.post(response_url, - json = {"text": text}, - headers = {"Content-type": "application/json"}) - if (resp.status_code != 200): - app.logger.error("Error posting request to Slack: " + resp.text) + if hmac.compare_digest(signature, slack_signature): + return True else: - slack_send_message(channel, text) - -def slack_send_message(channel, text): - """Send a Slack message to a specified channel.""" - - slack_client.chat_postMessage(channel=channel, text=text) - + print("Bad signature: {} != {}".format(signature, slack_signature)) + return False + +def slack_channel_members(slack_client, channel_id): + members = [] + + cursor = None + while True: + if cursor: + response = slack_client.conversations_members(channel=channel_id, + cursor=cursor) + else: + response = slack_client.conversations_members(channel=channel_id) + + if response['ok']: + members += response['members'] + else: + print("Error querying members of channel {}: {}" + .format(channel_id, response['error'])) + return members + + cursor = None + if 'next_cursor' in response['response_metadata']: + cursor = response['response_metadata']['next_cursor'] + + if not cursor or cursor == '': + break + + return members + +def slack_send_message(slack_client, channel_id, text, blocks=None): + if blocks: + slack_client.chat_postMessage(channel=channel_id, + text=text, blocks=blocks) + else: + slack_client.chat_postMessage(channel=channel_id, text=text)