Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 45 additions & 8 deletions slack_bolt/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ def __init__(
installation_store: Optional[InstallationStore] = None,
# for either only bot scope usage or v1.0.x compatibility
installation_store_bot_only: Optional[bool] = None,
# for customizing the built-in middleware
request_verification_enabled: bool = True,
ignoring_self_events_enabled: bool = True,
ssl_check_enabled: bool = True,
url_verification_enabled: bool = True,
# for the OAuth flow
oauth_settings: Optional[OAuthSettings] = None,
oauth_flow: Optional[OAuthFlow] = None,
Expand Down Expand Up @@ -146,6 +151,21 @@ def message_hello(message, say):
by checking if there is a team/user in the installation data.
installation_store: The module offering save/find operations of installation data
installation_store_bot_only: Use `InstallationStore#find_bot()` if True (Default: False)
request_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
`RequestVerification` is a built-in middleware that verifies the signature in HTTP Mode requests.
Make sure if it's safe enough when you turn a built-in middleware off.
We strongly recommend using RequestVerification for better security.
If you have a proxy that verifies request signature in front of the Bolt app,
it's totally fine to disable RequestVerification to avoid duplication of work.
Don't turn it off just for easiness of development.
ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True).
`IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events
generated by this app's bot user (this is useful for avoiding code error causing an infinite loop).
url_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
`UrlVerification` is a built-in middleware that handles url_verification requests
that verify the endpoint for Events API in HTTP Mode requests.
ssl_check_enabled: bool = False if you would like to disable the built-in middleware (Default: True).
`SslCheck` is a built-in middleware that handles ssl_check requests from Slack.
oauth_settings: The settings related to Slack app installation flow (OAuth flow)
oauth_flow: Instantiated `slack_bolt.oauth.OAuthFlow`. This is always prioritized over oauth_settings.
verification_token: Deprecated verification mechanism. This can used only for ssl_check requests.
Expand Down Expand Up @@ -292,22 +312,37 @@ def message_hello(message, say):

self._init_middleware_list_done = False
self._init_middleware_list(
token_verification_enabled=token_verification_enabled
token_verification_enabled=token_verification_enabled,
request_verification_enabled=request_verification_enabled,
ignoring_self_events_enabled=ignoring_self_events_enabled,
ssl_check_enabled=ssl_check_enabled,
url_verification_enabled=url_verification_enabled,
)

def _init_middleware_list(self, token_verification_enabled: bool):
def _init_middleware_list(
self,
token_verification_enabled: bool = True,
request_verification_enabled: bool = True,
ignoring_self_events_enabled: bool = True,
ssl_check_enabled: bool = True,
url_verification_enabled: bool = True,
):
if self._init_middleware_list_done:
return
self._middleware_list.append(
SslCheck(verification_token=self._verification_token)
)
self._middleware_list.append(RequestVerification(self._signing_secret))
if ssl_check_enabled is True:
self._middleware_list.append(
SslCheck(verification_token=self._verification_token)
)
if request_verification_enabled is True:
self._middleware_list.append(RequestVerification(self._signing_secret))

# As authorize is required for making a Bolt app function, we don't offer the flag to disable this
if self._oauth_flow is None:
if self._token is not None:
try:
auth_test_result = None
if token_verification_enabled:
# This API call is for eagerly validating the token
auth_test_result = self._client.auth_test(token=self._token)
self._middleware_list.append(
SingleTeamAuthorization(auth_test_result=auth_test_result)
Expand All @@ -324,8 +359,10 @@ def _init_middleware_list(self, token_verification_enabled: bool):
self._middleware_list.append(
MultiTeamsAuthorization(authorize=self._authorize)
)
self._middleware_list.append(IgnoringSelfEvents())
self._middleware_list.append(UrlVerification())
if ignoring_self_events_enabled is True:
self._middleware_list.append(IgnoringSelfEvents())
if url_verification_enabled is True:
self._middleware_list.append(UrlVerification())
self._init_middleware_list_done = True

# -------------------------
Expand Down
58 changes: 47 additions & 11 deletions slack_bolt/app/async_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,15 @@ def __init__(
token: Optional[str] = None,
client: Optional[AsyncWebClient] = None,
# for multi-workspace apps
authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None,
installation_store: Optional[AsyncInstallationStore] = None,
# for either only bot scope usage or v1.0.x compatibility
installation_store_bot_only: Optional[bool] = None,
authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None,
# for customizing the built-in middleware
request_verification_enabled: bool = True,
ignoring_self_events_enabled: bool = True,
ssl_check_enabled: bool = True,
url_verification_enabled: bool = True,
# for the OAuth flow
oauth_settings: Optional[AsyncOAuthSettings] = None,
oauth_flow: Optional[AsyncOAuthFlow] = None,
Expand Down Expand Up @@ -155,6 +160,21 @@ async def message_hello(message, say): # async function
by checking if there is a team/user in the installation data.
installation_store: The module offering save/find operations of installation data
installation_store_bot_only: Use `AsyncInstallationStore#async_find_bot()` if True (Default: False)
request_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
`AsyncRequestVerification` is a built-in middleware that verifies the signature in HTTP Mode requests.
Make sure if it's safe enough when you turn a built-in middleware off.
We strongly recommend using RequestVerification for better security.
If you have a proxy that verifies request signature in front of the Bolt app,
it's totally fine to disable RequestVerification to avoid duplication of work.
Don't turn it off just for easiness of development.
ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True).
`AsyncIgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events
generated by this app's bot user (this is useful for avoiding code error causing an infinite loop).
url_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
`AsyncUrlVerification` is a built-in middleware that handles url_verification requests
that verify the endpoint for Events API in HTTP Mode requests.
ssl_check_enabled: bool = False if you would like to disable the built-in middleware (Default: True).
`AsyncSslCheck` is a built-in middleware that handles ssl_check requests from Slack.
oauth_settings: The settings related to Slack app installation flow (OAuth flow)
oauth_flow: Instantiated `slack_bolt.oauth.AsyncOAuthFlow`. This is always prioritized over oauth_settings.
verification_token: Deprecated verification mechanism. This can used only for ssl_check requests.
Expand Down Expand Up @@ -316,19 +336,33 @@ async def message_hello(message, say): # async function
)

self._init_middleware_list_done = False
self._init_async_middleware_list()
self._init_async_middleware_list(
request_verification_enabled=request_verification_enabled,
ignoring_self_events_enabled=ignoring_self_events_enabled,
ssl_check_enabled=ssl_check_enabled,
url_verification_enabled=url_verification_enabled,
)

self._server: Optional[AsyncSlackAppServer] = None

def _init_async_middleware_list(self):
def _init_async_middleware_list(
self,
request_verification_enabled: bool = True,
ignoring_self_events_enabled: bool = True,
ssl_check_enabled: bool = True,
url_verification_enabled: bool = True,
):
if self._init_middleware_list_done:
return
self._async_middleware_list.append(
AsyncSslCheck(verification_token=self._verification_token)
)
self._async_middleware_list.append(
AsyncRequestVerification(self._signing_secret)
)
if ssl_check_enabled is True:
self._async_middleware_list.append(
AsyncSslCheck(verification_token=self._verification_token)
)
if request_verification_enabled is True:
self._async_middleware_list.append(
AsyncRequestVerification(self._signing_secret)
)
# As authorize is required for making a Bolt app function, we don't offer the flag to disable this
if self._async_oauth_flow is None:
if self._token:
self._async_middleware_list.append(AsyncSingleTeamAuthorization())
Expand All @@ -343,8 +377,10 @@ def _init_async_middleware_list(self):
AsyncMultiTeamsAuthorization(authorize=self._async_authorize)
)

self._async_middleware_list.append(AsyncIgnoringSelfEvents())
self._async_middleware_list.append(AsyncUrlVerification())
if ignoring_self_events_enabled is True:
self._async_middleware_list.append(AsyncIgnoringSelfEvents())
if url_verification_enabled is True:
self._async_middleware_list.append(AsyncUrlVerification())
self._init_middleware_list_done = True

# -------------------------
Expand Down
41 changes: 0 additions & 41 deletions tests/scenario_tests/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,47 +169,6 @@ def handle_app_mention():
response = app.dispatch(request)
assert response.status == 200

def test_self_events(self):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this to test_events_ignore_self.py

app = App(client=self.web_client, signing_secret=self.signing_secret)

event_body = {
"token": "verification_token",
"team_id": "T111",
"enterprise_id": "E111",
"api_app_id": "A111",
"event": {
"type": "reaction_added",
"user": "W23456789", # bot_user_id
"item": {
"type": "message",
"channel": "C111",
"ts": "1599529504.000400",
},
"reaction": "heart_eyes",
"item_user": "W111",
"event_ts": "1599616881.000800",
},
"type": "event_callback",
"event_id": "Ev111",
"event_time": 1599616881,
"authed_users": ["W111"],
}

@app.event("reaction_added")
def handle_app_mention(say):
say("What's up?")

timestamp, body = str(int(time())), json.dumps(event_body)
request: BoltRequest = BoltRequest(
body=body, headers=self.build_headers(timestamp, body)
)
response = app.dispatch(request)
assert response.status == 200
assert_auth_test_count(self, 1)
sleep(1) # wait a bit after auto ack()
# The listener should not be executed
assert self.mock_received_requests.get("/chat.postMessage") is None

def test_self_member_join_left_events(self):
app = App(client=self.web_client, signing_secret=self.signing_secret)

Expand Down
85 changes: 85 additions & 0 deletions tests/scenario_tests/test_events_ignore_self.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from time import sleep

from slack_sdk.web import WebClient

from slack_bolt import App, BoltRequest
from tests.mock_web_api_server import (
setup_mock_web_api_server,
cleanup_mock_web_api_server,
assert_auth_test_count,
)
from tests.utils import remove_os_env_temporarily, restore_os_env


class TestEventsIgnoreSelf:
valid_token = "xoxb-valid"
mock_api_server_base_url = "http://localhost:8888"
web_client = WebClient(
token=valid_token,
base_url=mock_api_server_base_url,
)

def setup_method(self):
self.old_os_env = remove_os_env_temporarily()
setup_mock_web_api_server(self)

def teardown_method(self):
cleanup_mock_web_api_server(self)
restore_os_env(self.old_os_env)

def test_self_events(self):
app = App(client=self.web_client)

@app.event("reaction_added")
def handle_app_mention(say):
say("What's up?")

request: BoltRequest = BoltRequest(body=event_body, mode="socket_mode")
response = app.dispatch(request)
assert response.status == 200
assert_auth_test_count(self, 1)
sleep(1) # wait a bit after auto ack()
# The listener should not be executed
assert self.mock_received_requests.get("/chat.postMessage") is None

def test_self_events_disabled(self):
app = App(
client=self.web_client,
ignoring_self_events_enabled=False,
)

@app.event("reaction_added")
def handle_app_mention(say):
say("What's up?")

request: BoltRequest = BoltRequest(body=event_body, mode="socket_mode")
response = app.dispatch(request)
assert response.status == 200
assert_auth_test_count(self, 1)
sleep(1) # wait a bit after auto ack()
# The listener should be executed as the ignoring logic is disabled
assert self.mock_received_requests.get("/chat.postMessage") == 1


event_body = {
"token": "verification_token",
"team_id": "T111",
"enterprise_id": "E111",
"api_app_id": "A111",
"event": {
"type": "reaction_added",
"user": "W23456789", # bot_user_id
"item": {
"type": "message",
"channel": "C111",
"ts": "1599529504.000400",
},
"reaction": "heart_eyes",
"item_user": "W111",
"event_ts": "1599616881.000800",
},
"type": "event_callback",
"event_id": "Ev111",
"event_time": 1599616881,
"authed_users": ["W111"],
}
Loading