Skip to content

Commit f012327

Browse files
committed
Fix slackapi#1074 Customize user-facing message sent when an installation is not managed by bolt-python app
1 parent e5131c9 commit f012327

File tree

11 files changed

+111
-42
lines changed

11 files changed

+111
-42
lines changed

scripts/install_all_and_run_tests.sh

+7-7
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,24 @@ python_version=`python --version | awk '{print $2}'`
1414

1515
if [ ${python_version:0:3} == "3.6" ]
1616
then
17-
pip install -r requirements.txt
17+
pip install -U -r requirements.txt
1818
else
1919
pip install -e .
2020
fi
2121

2222
if [[ $test_target != "" ]]
2323
then
24-
pip install -r requirements/testing.txt && \
25-
pip install -r requirements/adapter.txt && \
26-
pip install -r requirements/adapter_testing.txt && \
24+
pip install -U -r requirements/testing.txt && \
25+
pip install -U -r requirements/adapter.txt && \
26+
pip install -U -r requirements/adapter_testing.txt && \
2727
# To avoid errors due to the old versions of click forced by Chalice
2828
pip install -U pip click && \
2929
black slack_bolt/ tests/ && \
3030
pytest $1
3131
else
32-
pip install -r requirements/testing.txt && \
33-
pip install -r requirements/adapter.txt && \
34-
pip install -r requirements/adapter_testing.txt && \
32+
pip install -U -r requirements/testing.txt && \
33+
pip install -U -r requirements/adapter.txt && \
34+
pip install -U -r requirements/adapter_testing.txt && \
3535
# To avoid errors due to the old versions of click forced by Chalice
3636
pip install -U pip click && \
3737
black slack_bolt/ tests/ && \

slack_bolt/app/app.py

+13-2
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def __init__(
103103
# for multi-workspace apps
104104
before_authorize: Optional[Union[Middleware, Callable[..., Any]]] = None,
105105
authorize: Optional[Callable[..., AuthorizeResult]] = None,
106+
user_facing_authorize_error_message: Optional[str] = None,
106107
installation_store: Optional[InstallationStore] = None,
107108
# for either only bot scope usage or v1.0.x compatibility
108109
installation_store_bot_only: Optional[bool] = None,
@@ -159,6 +160,8 @@ def message_hello(message, say):
159160
before_authorize: A global middleware that can be executed right before authorize function
160161
authorize: The function to authorize an incoming request from Slack
161162
by checking if there is a team/user in the installation data.
163+
user_facing_authorize_error_message: The user-facing error message to display
164+
when the app is installed but the installation is not managed by this app's installation store
162165
installation_store: The module offering save/find operations of installation data
163166
installation_store_bot_only: Use `InstallationStore#find_bot()` if True (Default: False)
164167
request_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
@@ -178,7 +181,7 @@ def message_hello(message, say):
178181
`SslCheck` is a built-in middleware that handles ssl_check requests from Slack.
179182
oauth_settings: The settings related to Slack app installation flow (OAuth flow)
180183
oauth_flow: Instantiated `slack_bolt.oauth.OAuthFlow`. This is always prioritized over oauth_settings.
181-
verification_token: Deprecated verification mechanism. This can used only for ssl_check requests.
184+
verification_token: Deprecated verification mechanism. This can be used only for ssl_check requests.
182185
listener_executor: Custom executor to run background tasks. If absent, the default `ThreadPoolExecutor` will
183186
be used.
184187
"""
@@ -348,6 +351,7 @@ def message_hello(message, say):
348351
ignoring_self_events_enabled=ignoring_self_events_enabled,
349352
ssl_check_enabled=ssl_check_enabled,
350353
url_verification_enabled=url_verification_enabled,
354+
user_facing_authorize_error_message=user_facing_authorize_error_message,
351355
)
352356

353357
def _init_middleware_list(
@@ -357,6 +361,7 @@ def _init_middleware_list(
357361
ignoring_self_events_enabled: bool = True,
358362
ssl_check_enabled: bool = True,
359363
url_verification_enabled: bool = True,
364+
user_facing_authorize_error_message: Optional[str] = None,
360365
):
361366
if self._init_middleware_list_done:
362367
return
@@ -385,13 +390,18 @@ def _init_middleware_list(
385390
SingleTeamAuthorization(
386391
auth_test_result=auth_test_result,
387392
base_logger=self._base_logger,
393+
user_facing_authorize_error_message=user_facing_authorize_error_message,
388394
)
389395
)
390396
except SlackApiError as err:
391397
raise BoltError(error_auth_test_failure(err.response))
392398
elif self._authorize is not None:
393399
self._middleware_list.append(
394-
MultiTeamsAuthorization(authorize=self._authorize, base_logger=self._base_logger)
400+
MultiTeamsAuthorization(
401+
authorize=self._authorize,
402+
base_logger=self._base_logger,
403+
user_facing_authorize_error_message=user_facing_authorize_error_message,
404+
)
395405
)
396406
else:
397407
raise BoltError(error_token_required())
@@ -401,6 +411,7 @@ def _init_middleware_list(
401411
authorize=self._authorize,
402412
base_logger=self._base_logger,
403413
user_token_resolution=self._oauth_flow.settings.user_token_resolution,
414+
user_facing_authorize_error_message=user_facing_authorize_error_message,
404415
)
405416
)
406417
if ignoring_self_events_enabled is True:

slack_bolt/app/async_app.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ def __init__(
114114
# for multi-workspace apps
115115
before_authorize: Optional[Union[AsyncMiddleware, Callable[..., Awaitable[Any]]]] = None,
116116
authorize: Optional[Callable[..., Awaitable[AuthorizeResult]]] = None,
117+
user_facing_authorize_error_message: Optional[str] = None,
117118
installation_store: Optional[AsyncInstallationStore] = None,
118119
# for either only bot scope usage or v1.0.x compatibility
119120
installation_store_bot_only: Optional[bool] = None,
@@ -167,6 +168,8 @@ async def message_hello(message, say): # async function
167168
before_authorize: A global middleware that can be executed right before authorize function
168169
authorize: The function to authorize an incoming request from Slack
169170
by checking if there is a team/user in the installation data.
171+
user_facing_authorize_error_message: The user-facing error message to display
172+
when the app is installed but the installation is not managed by this app's installation store
170173
installation_store: The module offering save/find operations of installation data
171174
installation_store_bot_only: Use `AsyncInstallationStore#async_find_bot()` if True (Default: False)
172175
request_verification_enabled: False if you would like to disable the built-in middleware (Default: True).
@@ -354,6 +357,7 @@ async def message_hello(message, say): # async function
354357
ignoring_self_events_enabled=ignoring_self_events_enabled,
355358
ssl_check_enabled=ssl_check_enabled,
356359
url_verification_enabled=url_verification_enabled,
360+
user_facing_authorize_error_message=user_facing_authorize_error_message,
357361
)
358362

359363
self._server: Optional[AsyncSlackAppServer] = None
@@ -364,6 +368,7 @@ def _init_async_middleware_list(
364368
ignoring_self_events_enabled: bool = True,
365369
ssl_check_enabled: bool = True,
366370
url_verification_enabled: bool = True,
371+
user_facing_authorize_error_message: Optional[str] = None,
367372
):
368373
if self._init_middleware_list_done:
369374
return
@@ -383,10 +388,19 @@ def _init_async_middleware_list(
383388
# As authorize is required for making a Bolt app function, we don't offer the flag to disable this
384389
if self._async_oauth_flow is None:
385390
if self._token:
386-
self._async_middleware_list.append(AsyncSingleTeamAuthorization(base_logger=self._base_logger))
391+
self._async_middleware_list.append(
392+
AsyncSingleTeamAuthorization(
393+
base_logger=self._base_logger,
394+
user_facing_authorize_error_message=user_facing_authorize_error_message,
395+
)
396+
)
387397
elif self._async_authorize is not None:
388398
self._async_middleware_list.append(
389-
AsyncMultiTeamsAuthorization(authorize=self._async_authorize, base_logger=self._base_logger)
399+
AsyncMultiTeamsAuthorization(
400+
authorize=self._async_authorize,
401+
base_logger=self._base_logger,
402+
user_facing_authorize_error_message=user_facing_authorize_error_message,
403+
)
390404
)
391405
else:
392406
raise BoltError(error_token_required())
@@ -396,6 +410,7 @@ def _init_async_middleware_list(
396410
authorize=self._async_authorize,
397411
base_logger=self._base_logger,
398412
user_token_resolution=self._async_oauth_flow.settings.user_token_resolution,
413+
user_facing_authorize_error_message=user_facing_authorize_error_message,
399414
)
400415
)
401416

slack_bolt/middleware/authorization/async_internals.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from slack_bolt.middleware.authorization.internals import _build_error_text
21
from slack_bolt.request.async_request import AsyncBoltRequest
32
from slack_bolt.response import BoltResponse
43

@@ -15,9 +14,9 @@ def _is_no_auth_required(req: AsyncBoltRequest) -> bool:
1514
return _is_url_verification(req) or _is_ssl_check(req)
1615

1716

18-
def _build_error_response() -> BoltResponse:
17+
def _build_user_facing_error_response(message: str) -> BoltResponse:
1918
# show an ephemeral message to the end-user
2019
return BoltResponse(
2120
status=200,
22-
body=_build_error_text(),
21+
body=message,
2322
)

slack_bolt/middleware/authorization/async_multi_teams_authorization.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
from slack_bolt.request.async_request import AsyncBoltRequest
77
from slack_bolt.response import BoltResponse
88
from .async_authorization import AsyncAuthorization
9-
from .async_internals import _build_error_response, _is_no_auth_required
10-
from .internals import _is_no_auth_test_call_required, _build_error_text
9+
from .async_internals import _build_user_facing_error_response, _is_no_auth_required
10+
from .internals import _is_no_auth_test_call_required, _build_user_facing_authorize_error_message
1111
from ...authorization import AuthorizeResult
1212
from ...authorization.async_authorize import AsyncAuthorize
1313

@@ -21,17 +21,22 @@ def __init__(
2121
authorize: AsyncAuthorize,
2222
base_logger: Optional[Logger] = None,
2323
user_token_resolution: str = "authed_user",
24+
user_facing_authorize_error_message: Optional[str] = None,
2425
):
2526
"""Multi-workspace authorization.
2627
2728
Args:
2829
authorize: The function to authorize incoming requests from Slack.
2930
base_logger: The base logger
3031
user_token_resolution: "authed_user" or "actor"
32+
user_facing_authorize_error_message: The user-facing error message when installation is not found
3133
"""
3234
self.authorize = authorize
3335
self.logger = get_bolt_logger(AsyncMultiTeamsAuthorization, base_logger=base_logger)
3436
self.user_token_resolution = user_token_resolution
37+
self.user_facing_authorize_error_message = (
38+
user_facing_authorize_error_message or _build_user_facing_authorize_error_message()
39+
)
3540

3641
async def async_process(
3742
self,
@@ -92,10 +97,10 @@ async def async_process(
9297
"the AuthorizeResult (returned value from authorize) for it was not found."
9398
)
9499
if req.context.response_url is not None:
95-
await req.context.respond(_build_error_text())
100+
await req.context.respond(self.user_facing_authorize_error_message)
96101
return BoltResponse(status=200, body="")
97-
return _build_error_response()
102+
return _build_user_facing_error_response(self.user_facing_authorize_error_message)
98103

99104
except SlackApiError as e:
100105
self.logger.error(f"Failed to authorize with the given token ({e})")
101-
return _build_error_response()
106+
return _build_user_facing_error_response(self.user_facing_authorize_error_message)

slack_bolt/middleware/authorization/async_single_team_authorization.py

+13-6
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,23 @@
77
from slack_bolt.response import BoltResponse
88
from slack_sdk.web.async_slack_response import AsyncSlackResponse
99
from slack_sdk.errors import SlackApiError
10-
from .async_internals import _build_error_response, _is_no_auth_required
11-
from .internals import _to_authorize_result, _is_no_auth_test_call_required, _build_error_text
10+
from .async_internals import _build_user_facing_error_response, _is_no_auth_required
11+
from .internals import _to_authorize_result, _is_no_auth_test_call_required, _build_user_facing_authorize_error_message
1212
from ...authorization import AuthorizeResult
1313

1414

1515
class AsyncSingleTeamAuthorization(AsyncAuthorization):
16-
def __init__(self, base_logger: Optional[Logger] = None):
16+
def __init__(
17+
self,
18+
base_logger: Optional[Logger] = None,
19+
user_facing_authorize_error_message: Optional[str] = None,
20+
):
1721
"""Single-workspace authorization."""
1822
self.auth_test_result: Optional[AsyncSlackResponse] = None
1923
self.logger = get_bolt_logger(AsyncSingleTeamAuthorization, base_logger=base_logger)
24+
self.user_facing_authorize_error_message = (
25+
user_facing_authorize_error_message or _build_user_facing_authorize_error_message()
26+
)
2027

2128
async def async_process(
2229
self,
@@ -58,9 +65,9 @@ async def async_process(
5865
# Just in case
5966
self.logger.error("auth.test API call result is unexpectedly None")
6067
if req.context.response_url is not None:
61-
await req.context.respond(_build_error_text())
68+
await req.context.respond(self.user_facing_authorize_error_message)
6269
return BoltResponse(status=200, body="")
63-
return _build_error_response()
70+
return _build_user_facing_error_response(self.user_facing_authorize_error_message)
6471
except SlackApiError as e:
6572
self.logger.error(f"Failed to authorize with the given token ({e})")
66-
return _build_error_response()
73+
return _build_user_facing_error_response(self.user_facing_authorize_error_message)

slack_bolt/middleware/authorization/internals.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,18 @@ def _is_no_auth_test_call_required(req: Union[BoltRequest, "AsyncBoltRequest"])
4343
return _is_no_auth_test_events(req)
4444

4545

46-
def _build_error_text() -> str:
46+
def _build_user_facing_authorize_error_message() -> str:
4747
return (
4848
":warning: We apologize, but for some unknown reason, your installation with this app is no longer available. "
4949
"Please reinstall this app into your workspace :bow:"
5050
)
5151

5252

53-
def _build_error_response() -> BoltResponse:
53+
def _build_user_facing_error_response(message: str) -> BoltResponse:
5454
# show an ephemeral message to the end-user
5555
return BoltResponse(
5656
status=200,
57-
body=_build_error_text(),
57+
body=message,
5858
)
5959

6060

slack_bolt/middleware/authorization/multi_teams_authorization.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
from slack_bolt.response import BoltResponse
99
from .authorization import Authorization
1010
from .internals import (
11-
_build_error_response,
11+
_build_user_facing_error_response,
1212
_is_no_auth_required,
1313
_is_no_auth_test_call_required,
14-
_build_error_text,
14+
_build_user_facing_authorize_error_message,
1515
)
1616
from ...authorization import AuthorizeResult
1717
from ...authorization.authorize import Authorize
@@ -27,17 +27,22 @@ def __init__(
2727
authorize: Authorize,
2828
base_logger: Optional[Logger] = None,
2929
user_token_resolution: str = "authed_user",
30+
user_facing_authorize_error_message: Optional[str] = None,
3031
):
3132
"""Multi-workspace authorization.
3233
3334
Args:
3435
authorize: The function to authorize incoming requests from Slack.
3536
base_logger: The base logger
3637
user_token_resolution: "authed_user" or "actor"
38+
user_facing_authorize_error_message: The user-facing error message when installation is not found
3739
"""
3840
self.authorize = authorize
3941
self.logger = get_bolt_logger(MultiTeamsAuthorization, base_logger=base_logger)
4042
self.user_token_resolution = user_token_resolution
43+
self.user_facing_authorize_error_message = (
44+
user_facing_authorize_error_message or _build_user_facing_authorize_error_message()
45+
)
4146

4247
def process(
4348
self,
@@ -95,10 +100,10 @@ def process(
95100
"the AuthorizeResult (returned value from authorize) for it was not found."
96101
)
97102
if req.context.response_url is not None:
98-
req.context.respond(_build_error_text())
103+
req.context.respond(self.user_facing_authorize_error_message)
99104
return BoltResponse(status=200, body="")
100-
return _build_error_response()
105+
return _build_user_facing_error_response(self.user_facing_authorize_error_message)
101106

102107
except SlackApiError as e:
103108
self.logger.error(f"Failed to authorize with the given token ({e})")
104-
return _build_error_response()
109+
return _build_user_facing_error_response(self.user_facing_authorize_error_message)

slack_bolt/middleware/authorization/single_team_authorization.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
from slack_sdk.errors import SlackApiError
99
from slack_sdk.web import SlackResponse
1010
from .internals import (
11-
_build_error_response,
11+
_build_user_facing_error_response,
1212
_is_no_auth_required,
1313
_to_authorize_result,
1414
_is_no_auth_test_call_required,
15-
_build_error_text,
15+
_build_user_facing_authorize_error_message,
1616
)
1717
from ...authorization import AuthorizeResult
1818

@@ -23,6 +23,7 @@ def __init__(
2323
*,
2424
auth_test_result: Optional[SlackResponse] = None,
2525
base_logger: Optional[Logger] = None,
26+
user_facing_authorize_error_message: Optional[str] = None,
2627
):
2728
"""Single-workspace authorization.
2829
@@ -32,6 +33,9 @@ def __init__(
3233
"""
3334
self.auth_test_result = auth_test_result
3435
self.logger = get_bolt_logger(SingleTeamAuthorization, base_logger=base_logger)
36+
self.user_facing_authorize_error_message = (
37+
user_facing_authorize_error_message or _build_user_facing_authorize_error_message()
38+
)
3539

3640
def process(
3741
self,
@@ -73,9 +77,9 @@ def process(
7377
# Just in case
7478
self.logger.error("auth.test API call result is unexpectedly None")
7579
if req.context.response_url is not None:
76-
req.context.respond(_build_error_text())
80+
req.context.respond(self.user_facing_authorize_error_message)
7781
return BoltResponse(status=200, body="")
78-
return _build_error_response()
82+
return _build_user_facing_error_response(self.user_facing_authorize_error_message)
7983
except SlackApiError as e:
8084
self.logger.error(f"Failed to authorize with the given token ({e})")
81-
return _build_error_response()
85+
return _build_user_facing_error_response(self.user_facing_authorize_error_message)

0 commit comments

Comments
 (0)