Skip to content

Commit 76158a8

Browse files
authored
Fix #307 Add options to disable the built-in middleware (#310)
1 parent ad622e4 commit 76158a8

File tree

11 files changed

+692
-101
lines changed

11 files changed

+692
-101
lines changed

slack_bolt/app/app.py

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ def __init__(
9999
installation_store: Optional[InstallationStore] = None,
100100
# for either only bot scope usage or v1.0.x compatibility
101101
installation_store_bot_only: Optional[bool] = None,
102+
# for customizing the built-in middleware
103+
request_verification_enabled: bool = True,
104+
ignoring_self_events_enabled: bool = True,
105+
ssl_check_enabled: bool = True,
106+
url_verification_enabled: bool = True,
102107
# for the OAuth flow
103108
oauth_settings: Optional[OAuthSettings] = None,
104109
oauth_flow: Optional[OAuthFlow] = None,
@@ -146,6 +151,21 @@ def message_hello(message, say):
146151
by checking if there is a team/user in the installation data.
147152
installation_store: The module offering save/find operations of installation data
148153
installation_store_bot_only: Use `InstallationStore#find_bot()` if True (Default: False)
154+
request_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
155+
`RequestVerification` is a built-in middleware that verifies the signature in HTTP Mode requests.
156+
Make sure if it's safe enough when you turn a built-in middleware off.
157+
We strongly recommend using RequestVerification for better security.
158+
If you have a proxy that verifies request signature in front of the Bolt app,
159+
it's totally fine to disable RequestVerification to avoid duplication of work.
160+
Don't turn it off just for easiness of development.
161+
ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True).
162+
`IgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events
163+
generated by this app's bot user (this is useful for avoiding code error causing an infinite loop).
164+
url_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
165+
`UrlVerification` is a built-in middleware that handles url_verification requests
166+
that verify the endpoint for Events API in HTTP Mode requests.
167+
ssl_check_enabled: bool = False if you would like to disable the built-in middleware (Default: True).
168+
`SslCheck` is a built-in middleware that handles ssl_check requests from Slack.
149169
oauth_settings: The settings related to Slack app installation flow (OAuth flow)
150170
oauth_flow: Instantiated `slack_bolt.oauth.OAuthFlow`. This is always prioritized over oauth_settings.
151171
verification_token: Deprecated verification mechanism. This can used only for ssl_check requests.
@@ -292,22 +312,37 @@ def message_hello(message, say):
292312

293313
self._init_middleware_list_done = False
294314
self._init_middleware_list(
295-
token_verification_enabled=token_verification_enabled
315+
token_verification_enabled=token_verification_enabled,
316+
request_verification_enabled=request_verification_enabled,
317+
ignoring_self_events_enabled=ignoring_self_events_enabled,
318+
ssl_check_enabled=ssl_check_enabled,
319+
url_verification_enabled=url_verification_enabled,
296320
)
297321

298-
def _init_middleware_list(self, token_verification_enabled: bool):
322+
def _init_middleware_list(
323+
self,
324+
token_verification_enabled: bool = True,
325+
request_verification_enabled: bool = True,
326+
ignoring_self_events_enabled: bool = True,
327+
ssl_check_enabled: bool = True,
328+
url_verification_enabled: bool = True,
329+
):
299330
if self._init_middleware_list_done:
300331
return
301-
self._middleware_list.append(
302-
SslCheck(verification_token=self._verification_token)
303-
)
304-
self._middleware_list.append(RequestVerification(self._signing_secret))
332+
if ssl_check_enabled is True:
333+
self._middleware_list.append(
334+
SslCheck(verification_token=self._verification_token)
335+
)
336+
if request_verification_enabled is True:
337+
self._middleware_list.append(RequestVerification(self._signing_secret))
305338

339+
# As authorize is required for making a Bolt app function, we don't offer the flag to disable this
306340
if self._oauth_flow is None:
307341
if self._token is not None:
308342
try:
309343
auth_test_result = None
310344
if token_verification_enabled:
345+
# This API call is for eagerly validating the token
311346
auth_test_result = self._client.auth_test(token=self._token)
312347
self._middleware_list.append(
313348
SingleTeamAuthorization(auth_test_result=auth_test_result)
@@ -324,8 +359,10 @@ def _init_middleware_list(self, token_verification_enabled: bool):
324359
self._middleware_list.append(
325360
MultiTeamsAuthorization(authorize=self._authorize)
326361
)
327-
self._middleware_list.append(IgnoringSelfEvents())
328-
self._middleware_list.append(UrlVerification())
362+
if ignoring_self_events_enabled is True:
363+
self._middleware_list.append(IgnoringSelfEvents())
364+
if url_verification_enabled is True:
365+
self._middleware_list.append(UrlVerification())
329366
self._init_middleware_list_done = True
330367

331368
# -------------------------

slack_bolt/app/async_app.py

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,15 @@ def __init__(
105105
token: Optional[str] = None,
106106
client: Optional[AsyncWebClient] = None,
107107
# for multi-workspace apps
108+
authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None,
108109
installation_store: Optional[AsyncInstallationStore] = None,
109110
# for either only bot scope usage or v1.0.x compatibility
110111
installation_store_bot_only: Optional[bool] = None,
111-
authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None,
112+
# for customizing the built-in middleware
113+
request_verification_enabled: bool = True,
114+
ignoring_self_events_enabled: bool = True,
115+
ssl_check_enabled: bool = True,
116+
url_verification_enabled: bool = True,
112117
# for the OAuth flow
113118
oauth_settings: Optional[AsyncOAuthSettings] = None,
114119
oauth_flow: Optional[AsyncOAuthFlow] = None,
@@ -155,6 +160,21 @@ async def message_hello(message, say): # async function
155160
by checking if there is a team/user in the installation data.
156161
installation_store: The module offering save/find operations of installation data
157162
installation_store_bot_only: Use `AsyncInstallationStore#async_find_bot()` if True (Default: False)
163+
request_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
164+
`AsyncRequestVerification` is a built-in middleware that verifies the signature in HTTP Mode requests.
165+
Make sure if it's safe enough when you turn a built-in middleware off.
166+
We strongly recommend using RequestVerification for better security.
167+
If you have a proxy that verifies request signature in front of the Bolt app,
168+
it's totally fine to disable RequestVerification to avoid duplication of work.
169+
Don't turn it off just for easiness of development.
170+
ignoring_self_events_enabled: False if you would like to disable the built-in middleware (Default: True).
171+
`AsyncIgnoringSelfEvents` is a built-in middleware that enables Bolt apps to easily skip the events
172+
generated by this app's bot user (this is useful for avoiding code error causing an infinite loop).
173+
url_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
174+
`AsyncUrlVerification` is a built-in middleware that handles url_verification requests
175+
that verify the endpoint for Events API in HTTP Mode requests.
176+
ssl_check_enabled: bool = False if you would like to disable the built-in middleware (Default: True).
177+
`AsyncSslCheck` is a built-in middleware that handles ssl_check requests from Slack.
158178
oauth_settings: The settings related to Slack app installation flow (OAuth flow)
159179
oauth_flow: Instantiated `slack_bolt.oauth.AsyncOAuthFlow`. This is always prioritized over oauth_settings.
160180
verification_token: Deprecated verification mechanism. This can used only for ssl_check requests.
@@ -316,19 +336,33 @@ async def message_hello(message, say): # async function
316336
)
317337

318338
self._init_middleware_list_done = False
319-
self._init_async_middleware_list()
339+
self._init_async_middleware_list(
340+
request_verification_enabled=request_verification_enabled,
341+
ignoring_self_events_enabled=ignoring_self_events_enabled,
342+
ssl_check_enabled=ssl_check_enabled,
343+
url_verification_enabled=url_verification_enabled,
344+
)
320345

321346
self._server: Optional[AsyncSlackAppServer] = None
322347

323-
def _init_async_middleware_list(self):
348+
def _init_async_middleware_list(
349+
self,
350+
request_verification_enabled: bool = True,
351+
ignoring_self_events_enabled: bool = True,
352+
ssl_check_enabled: bool = True,
353+
url_verification_enabled: bool = True,
354+
):
324355
if self._init_middleware_list_done:
325356
return
326-
self._async_middleware_list.append(
327-
AsyncSslCheck(verification_token=self._verification_token)
328-
)
329-
self._async_middleware_list.append(
330-
AsyncRequestVerification(self._signing_secret)
331-
)
357+
if ssl_check_enabled is True:
358+
self._async_middleware_list.append(
359+
AsyncSslCheck(verification_token=self._verification_token)
360+
)
361+
if request_verification_enabled is True:
362+
self._async_middleware_list.append(
363+
AsyncRequestVerification(self._signing_secret)
364+
)
365+
# As authorize is required for making a Bolt app function, we don't offer the flag to disable this
332366
if self._async_oauth_flow is None:
333367
if self._token:
334368
self._async_middleware_list.append(AsyncSingleTeamAuthorization())
@@ -343,8 +377,10 @@ def _init_async_middleware_list(self):
343377
AsyncMultiTeamsAuthorization(authorize=self._async_authorize)
344378
)
345379

346-
self._async_middleware_list.append(AsyncIgnoringSelfEvents())
347-
self._async_middleware_list.append(AsyncUrlVerification())
380+
if ignoring_self_events_enabled is True:
381+
self._async_middleware_list.append(AsyncIgnoringSelfEvents())
382+
if url_verification_enabled is True:
383+
self._async_middleware_list.append(AsyncUrlVerification())
348384
self._init_middleware_list_done = True
349385

350386
# -------------------------

tests/scenario_tests/test_events.py

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -169,47 +169,6 @@ def handle_app_mention():
169169
response = app.dispatch(request)
170170
assert response.status == 200
171171

172-
def test_self_events(self):
173-
app = App(client=self.web_client, signing_secret=self.signing_secret)
174-
175-
event_body = {
176-
"token": "verification_token",
177-
"team_id": "T111",
178-
"enterprise_id": "E111",
179-
"api_app_id": "A111",
180-
"event": {
181-
"type": "reaction_added",
182-
"user": "W23456789", # bot_user_id
183-
"item": {
184-
"type": "message",
185-
"channel": "C111",
186-
"ts": "1599529504.000400",
187-
},
188-
"reaction": "heart_eyes",
189-
"item_user": "W111",
190-
"event_ts": "1599616881.000800",
191-
},
192-
"type": "event_callback",
193-
"event_id": "Ev111",
194-
"event_time": 1599616881,
195-
"authed_users": ["W111"],
196-
}
197-
198-
@app.event("reaction_added")
199-
def handle_app_mention(say):
200-
say("What's up?")
201-
202-
timestamp, body = str(int(time())), json.dumps(event_body)
203-
request: BoltRequest = BoltRequest(
204-
body=body, headers=self.build_headers(timestamp, body)
205-
)
206-
response = app.dispatch(request)
207-
assert response.status == 200
208-
assert_auth_test_count(self, 1)
209-
sleep(1) # wait a bit after auto ack()
210-
# The listener should not be executed
211-
assert self.mock_received_requests.get("/chat.postMessage") is None
212-
213172
def test_self_member_join_left_events(self):
214173
app = App(client=self.web_client, signing_secret=self.signing_secret)
215174

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
from time import sleep
2+
3+
from slack_sdk.web import WebClient
4+
5+
from slack_bolt import App, BoltRequest
6+
from tests.mock_web_api_server import (
7+
setup_mock_web_api_server,
8+
cleanup_mock_web_api_server,
9+
assert_auth_test_count,
10+
)
11+
from tests.utils import remove_os_env_temporarily, restore_os_env
12+
13+
14+
class TestEventsIgnoreSelf:
15+
valid_token = "xoxb-valid"
16+
mock_api_server_base_url = "http://localhost:8888"
17+
web_client = WebClient(
18+
token=valid_token,
19+
base_url=mock_api_server_base_url,
20+
)
21+
22+
def setup_method(self):
23+
self.old_os_env = remove_os_env_temporarily()
24+
setup_mock_web_api_server(self)
25+
26+
def teardown_method(self):
27+
cleanup_mock_web_api_server(self)
28+
restore_os_env(self.old_os_env)
29+
30+
def test_self_events(self):
31+
app = App(client=self.web_client)
32+
33+
@app.event("reaction_added")
34+
def handle_app_mention(say):
35+
say("What's up?")
36+
37+
request: BoltRequest = BoltRequest(body=event_body, mode="socket_mode")
38+
response = app.dispatch(request)
39+
assert response.status == 200
40+
assert_auth_test_count(self, 1)
41+
sleep(1) # wait a bit after auto ack()
42+
# The listener should not be executed
43+
assert self.mock_received_requests.get("/chat.postMessage") is None
44+
45+
def test_self_events_disabled(self):
46+
app = App(
47+
client=self.web_client,
48+
ignoring_self_events_enabled=False,
49+
)
50+
51+
@app.event("reaction_added")
52+
def handle_app_mention(say):
53+
say("What's up?")
54+
55+
request: BoltRequest = BoltRequest(body=event_body, mode="socket_mode")
56+
response = app.dispatch(request)
57+
assert response.status == 200
58+
assert_auth_test_count(self, 1)
59+
sleep(1) # wait a bit after auto ack()
60+
# The listener should be executed as the ignoring logic is disabled
61+
assert self.mock_received_requests.get("/chat.postMessage") == 1
62+
63+
64+
event_body = {
65+
"token": "verification_token",
66+
"team_id": "T111",
67+
"enterprise_id": "E111",
68+
"api_app_id": "A111",
69+
"event": {
70+
"type": "reaction_added",
71+
"user": "W23456789", # bot_user_id
72+
"item": {
73+
"type": "message",
74+
"channel": "C111",
75+
"ts": "1599529504.000400",
76+
},
77+
"reaction": "heart_eyes",
78+
"item_user": "W111",
79+
"event_ts": "1599616881.000800",
80+
},
81+
"type": "event_callback",
82+
"event_id": "Ev111",
83+
"event_time": 1599616881,
84+
"authed_users": ["W111"],
85+
}

0 commit comments

Comments
 (0)