Skip to content

Commit

Permalink
Adding changes to not send to a disabled webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
sylviamclaughlin authored Mar 26, 2024
1 parent 8e9bec0 commit 81e0755
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 98 deletions.
9 changes: 9 additions & 0 deletions app/models/webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ def revoke_webhook(id):
return response


# function to return the status of the webhook (ie if it is active or not). If active, return True, else return False
def is_active(id):
response = client.get_item(TableName=table, Key={"id": {"S": id}})
if "Item" in response:
return response["Item"]["active"]["BOOL"]
else:
return False


def toggle_webhook(id):
response = client.update_item(
TableName=table,
Expand Down
2 changes: 1 addition & 1 deletion app/modules/sre/webhook_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ def toggle_webhook(ack, body, logger, client):
channel = hook["channel"]["S"]

webhooks.toggle_webhook(id)
message = f"Webhook {name} has been {'enabled' if hook['active']['BOOL'] else 'disabled'} by <@{username}>"
message = f"Webhook {name} has been {'disabled' if hook['active']['BOOL'] else 'enabled'} by <@{username}>"
logger.info(message)
client.chat_postMessage(
channel=channel,
Expand Down
184 changes: 94 additions & 90 deletions app/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,102 +186,106 @@ def geolocate(ip):
def handle_webhook(id: str, payload: WebhookPayload | str, request: Request):
webhook = webhooks.get_webhook(id)
if webhook:
webhooks.increment_invocation_count(id)

if isinstance(payload, str):
# if the webhook is active, then send forward the response to the webhook
if webhooks.is_active(id):
webhooks.increment_invocation_count(id)
if isinstance(payload, str):
try:
payload = AwsSnsPayload.parse_raw(payload)
sns_message_validator.validate_message(message=payload.dict())
except InvalidMessageTypeException as e:
logging.error(e)
log_ops_message(
request.state.bot.client,
f"Invalid message type ```{payload.Type}``` in message: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
)
except InvalidSignatureVersionException as e:
logging.error(e)
log_ops_message(
request.state.bot.client,
f"Unexpected signature version ```{payload.SignatureVersion}``` in message: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
)
except SignatureVerificationFailureException as e:
logging.error(e)
log_ops_message(
request.state.bot.client,
f"Failed to verify signature ```{payload.Signature}``` in message: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
)
except InvalidCertURLException as e:
logging.error(e)
log_ops_message(
request.state.bot.client,
f"Invalid certificate URL ```{payload.SigningCertURL}``` in message: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
)
except Exception as e:
logging.error(e)
log_ops_message(
request.state.bot.client,
f"Error parsing AWS event due to {e.__class__.__qualname__}: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
)
if payload.Type == "SubscriptionConfirmation":
requests.get(payload.SubscribeURL, timeout=60)
logging.info(f"Subscribed webhook {id} to topic {payload.TopicArn}")
log_ops_message(
request.state.bot.client,
f"Subscribed webhook {id} to topic {payload.TopicArn}",
)
return {"ok": True}

if payload.Type == "UnsubscribeConfirmation":
log_ops_message(
request.state.bot.client,
f"{payload.TopicArn} unsubscribed from webhook {id}",
)
return {"ok": True}

if payload.Type == "Notification":
blocks = aws.parse(payload, request.state.bot.client)
# if we have an empty message, log that we have an empty
# message and return without posting to slack
if not blocks:
logging.info("No blocks to post, returning")
return
payload = WebhookPayload(blocks=blocks)
payload.channel = webhook["channel"]["S"]
payload = append_incident_buttons(payload, id)
try:
payload = AwsSnsPayload.parse_raw(payload)
sns_message_validator.validate_message(message=payload.dict())
except InvalidMessageTypeException as e:
logging.error(e)
log_ops_message(
request.state.bot.client,
f"Invalid message type ```{payload.Type}``` in message: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
)
except InvalidSignatureVersionException as e:
logging.error(e)
log_ops_message(
request.state.bot.client,
f"Unexpected signature version ```{payload.SignatureVersion}``` in message: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
)
except SignatureVerificationFailureException as e:
logging.error(e)
log_ops_message(
request.state.bot.client,
f"Failed to verify signature ```{payload.Signature}``` in message: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
)
except InvalidCertURLException as e:
logging.error(e)
log_ops_message(
request.state.bot.client,
f"Invalid certificate URL ```{payload.SigningCertURL}``` in message: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
message = json.loads(payload.json(exclude_none=True))
request.state.bot.client.api_call("chat.postMessage", json=message)
log_to_sentinel(
"webhook_sent", {"webhook": webhook, "payload": payload.dict()}
)
return {"ok": True}
except Exception as e:
logging.error(e)
body = payload.json(exclude_none=True)
log_ops_message(
request.state.bot.client,
f"Error parsing AWS event due to {e.__class__.__qualname__}: ```{payload}```",
)
raise HTTPException(
status_code=500,
detail=f"Failed to parse AWS event message due to {e.__class__.__qualname__}: {e}",
)
if payload.Type == "SubscriptionConfirmation":
requests.get(payload.SubscribeURL, timeout=60)
logging.info(f"Subscribed webhook {id} to topic {payload.TopicArn}")
log_ops_message(
request.state.bot.client,
f"Subscribed webhook {id} to topic {payload.TopicArn}",
request.state.bot.client, f"Error posting message: ```{body}```"
)
return {"ok": True}

if payload.Type == "UnsubscribeConfirmation":
log_ops_message(
request.state.bot.client,
f"{payload.TopicArn} unsubscribed from webhook {id}",
)
return {"ok": True}

if payload.Type == "Notification":
blocks = aws.parse(payload, request.state.bot.client)
# if we have an empty message, log that we have an empty
# message and return without posting to slack
if not blocks:
logging.info("No blocks to post, returning")
return
payload = WebhookPayload(blocks=blocks)
payload.channel = webhook["channel"]["S"]
payload = append_incident_buttons(payload, id)
try:
message = json.loads(payload.json(exclude_none=True))
request.state.bot.client.api_call("chat.postMessage", json=message)
log_to_sentinel(
"webhook_sent", {"webhook": webhook, "payload": payload.dict()}
)
return {"ok": True}
except Exception as e:
logging.error(e)
body = payload.json(exclude_none=True)
log_ops_message(
request.state.bot.client, f"Error posting message: ```{body}```"
)
raise HTTPException(status_code=500, detail="Failed to send message")
raise HTTPException(status_code=500, detail="Failed to send message")
else:
logging.info(f"Webhook id {id} is not active")
raise HTTPException(status_code=404, detail="Webhook not active")
else:
raise HTTPException(status_code=404, detail="Webhook not found")

Expand Down
40 changes: 40 additions & 0 deletions app/tests/models/test_webhooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,46 @@ def test_revoke_webhook(client_mock):
)


@patch("models.webhooks.client")
def test_is_active_returns_true(client_mock):
client_mock.get_item.return_value = {
"Item": {
"id": {"S": "test_id"},
"channel": {"S": "test_channel"},
"name": {"S": "test_name"},
"created_at": {"S": "test_created_at"},
"active": {"BOOL": True},
"user_id": {"S": "test_user_id"},
"invocation_count": {"N": "0"},
"acknowledged_count": {"N": "0"},
}
}
assert webhooks.is_active("test_id") is True


@patch("models.webhooks.client")
def test_is_active_returns_false(client_mock):
client_mock.get_item.return_value = {
"Item": {
"id": {"S": "test_id"},
"channel": {"S": "test_channel"},
"name": {"S": "test_name"},
"created_at": {"S": "test_created_at"},
"active": {"BOOL": False},
"user_id": {"S": "test_user_id"},
"invocation_count": {"N": "0"},
"acknowledged_count": {"N": "0"},
}
}
assert webhooks.is_active("test_id") is False


@patch("models.webhooks.client")
def test_is_active_not_found(client_mock):
client_mock.get_item.return_value = {}
assert webhooks.is_active("test_id") is False


@patch("models.webhooks.client")
@patch("models.webhooks.get_webhook")
def test_toggle_webhook(get_webhook_mock, client_mock):
Expand Down
4 changes: 2 additions & 2 deletions app/tests/modules/sre/test_webhook_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,11 +505,11 @@ def test_toggle_webhook(list_all_webhooks_mock, toggle_webhook_mock, get_webhook
webhook_helper.toggle_webhook(ack, body, logger, client)
ack.assert_called()
toggle_webhook_mock.assert_called_with("id")
logger.info.assert_called_with("Webhook name has been enabled by <@username>")
logger.info.assert_called_with("Webhook name has been disabled by <@username>")
client.chat_postMessage.assert_called_with(
channel="channel",
user="user_id",
text="Webhook name has been enabled by <@username>",
text="Webhook name has been disabled by <@username>",
)
list_all_webhooks_mock.assert_called_with(
client, body, 0, webhook_helper.MAX_BLOCK_SIZE, "all", update=True
Expand Down
Loading

0 comments on commit 81e0755

Please sign in to comment.