Skip to content

Commit 8a6cf2e

Browse files
committed
Fix slackapi#98 by adding authorize function support
1 parent 9968804 commit 8a6cf2e

22 files changed

Lines changed: 644 additions & 129 deletions

samples/app_authorize.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# ------------------------------------------------
2+
# instead of slack_bolt in requirements.txt
3+
import sys
4+
5+
6+
sys.path.insert(1, "..")
7+
# ------------------------------------------------
8+
9+
import logging
10+
logging.basicConfig(level=logging.DEBUG)
11+
12+
import os
13+
from slack_bolt import App
14+
from slack_bolt.authorization import AuthorizationResult
15+
from slack_sdk import WebClient
16+
17+
def authorize(enterprise_id, team_id, user_id, client: WebClient, logger):
18+
logger.info(f"{enterprise_id},{team_id},{user_id}")
19+
# You can implement your own logic here
20+
token = os.environ["MY_TOKEN"]
21+
return AuthorizationResult.from_auth_test_response(
22+
auth_test_response=client.auth_test(token=token),
23+
bot_token=token,
24+
)
25+
26+
app = App(
27+
signing_secret=os.environ["SLACK_SIGNING_SECRET"],
28+
authorize=authorize
29+
)
30+
31+
@app.command("/hello-bolt-python")
32+
def hello_command(ack, body):
33+
user_id = body["user_id"]
34+
ack(f"Hi <@{user_id}>!")
35+
36+
37+
@app.event("app_mention")
38+
def event_test(body, say, logger):
39+
logger.info(body)
40+
say("What's up?")
41+
42+
43+
if __name__ == "__main__":
44+
app.start(3000)
45+
46+
# pip install slack_bolt
47+
# export SLACK_SIGNING_SECRET=***
48+
# export MY_TOKEN=xoxb-***
49+
# python app.py

samples/async_app_authorize.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# ------------------------------------------------
2+
# instead of slack_bolt in requirements.txt
3+
import sys
4+
5+
sys.path.insert(1, "..")
6+
# ------------------------------------------------
7+
8+
import logging
9+
10+
logging.basicConfig(level=logging.DEBUG)
11+
12+
import os
13+
from slack_sdk.web.async_client import AsyncWebClient
14+
from slack_bolt.authorization import AuthorizationResult
15+
from slack_bolt.async_app import AsyncApp
16+
17+
async def authorize(enterprise_id, team_id, user_id, client: AsyncWebClient, logger):
18+
logger.info(f"{enterprise_id},{team_id},{user_id}")
19+
# You can implement your own logic here
20+
token = os.environ["MY_TOKEN"]
21+
return AuthorizationResult.from_auth_test_response(
22+
auth_test_response=await client.auth_test(token=token),
23+
bot_token=token,
24+
)
25+
26+
27+
app = AsyncApp(
28+
signing_secret=os.environ["SLACK_SIGNING_SECRET"],
29+
authorize=authorize
30+
)
31+
32+
@app.event("app_mention")
33+
async def event_test(body, say, logger):
34+
logger.info(body)
35+
await say("What's up?")
36+
37+
38+
@app.command("/hello-bolt-python")
39+
# or app.command(re.compile(r"/hello-.+"))(test_command)
40+
async def command(ack, body):
41+
user_id = body["user_id"]
42+
await ack(f"Hi <@{user_id}>!")
43+
44+
45+
if __name__ == "__main__":
46+
app.start(3000)
47+
48+
# pip install slack_bolt
49+
# export SLACK_SIGNING_SECRET=***
50+
# export MY_TOKEN=xoxb-***
51+
# python app.py

slack_bolt/app/app.py

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,30 @@
1111
from slack_sdk.oauth.installation_store import InstallationStore
1212
from slack_sdk.web import WebClient
1313

14+
from slack_bolt.authorization import AuthorizationResult
15+
from slack_bolt.authorization.authorize import (
16+
Authorize,
17+
InstallationStoreAuthorize,
18+
CallableAuthorize,
19+
)
1420
from slack_bolt.error import BoltError
21+
from slack_bolt.lazy_listener.runner import LazyListenerRunner
22+
from slack_bolt.lazy_listener.thread_runner import ThreadLazyListenerRunner
23+
from slack_bolt.listener.custom_listener import CustomListener
24+
from slack_bolt.listener.listener import Listener
25+
from slack_bolt.listener.listener_error_handler import (
26+
ListenerErrorHandler,
27+
DefaultListenerErrorHandler,
28+
CustomListenerErrorHandler,
29+
)
30+
from slack_bolt.listener_matcher import CustomListenerMatcher
31+
from slack_bolt.listener_matcher import builtins as builtin_matchers
32+
from slack_bolt.listener_matcher.listener_matcher import ListenerMatcher
33+
from slack_bolt.logger import get_bolt_app_logger, get_bolt_logger
1534
from slack_bolt.logger.messages import (
1635
error_signing_secret_not_found,
1736
warning_client_prioritized_and_token_skipped,
18-
warning_installation_store_prioritized_and_token_skipped,
37+
warning_token_skipped,
1938
error_auth_test_failure,
2039
error_token_required,
2140
warning_unhandled_request,
@@ -28,19 +47,6 @@
2847
error_unexpected_listener_middleware,
2948
error_client_invalid_type,
3049
)
31-
from slack_bolt.lazy_listener.runner import LazyListenerRunner
32-
from slack_bolt.lazy_listener.thread_runner import ThreadLazyListenerRunner
33-
from slack_bolt.listener.custom_listener import CustomListener
34-
from slack_bolt.listener.listener import Listener
35-
from slack_bolt.listener.listener_error_handler import (
36-
ListenerErrorHandler,
37-
DefaultListenerErrorHandler,
38-
CustomListenerErrorHandler,
39-
)
40-
from slack_bolt.listener_matcher import CustomListenerMatcher
41-
from slack_bolt.listener_matcher import builtins as builtin_matchers
42-
from slack_bolt.listener_matcher.listener_matcher import ListenerMatcher
43-
from slack_bolt.logger import get_bolt_app_logger, get_bolt_logger
4450
from slack_bolt.middleware import (
4551
Middleware,
4652
SslCheck,
@@ -73,6 +79,7 @@ def __init__(
7379
token: Optional[str] = None,
7480
client: Optional[WebClient] = None,
7581
# for multi-workspace apps
82+
authorize: Optional[Callable[..., AuthorizationResult]] = None,
7683
installation_store: Optional[InstallationStore] = None,
7784
# for the OAuth flow
7885
oauth_settings: Optional[OAuthSettings] = None,
@@ -88,6 +95,8 @@ def __init__(
8895
:param signing_secret: The Signing Secret value used for verifying requests from Slack.
8996
:param token: The bot access token required only for single-workspace app.
9097
:param client: The singleton slack_sdk.WebClient instance for this app.
98+
:param authorize: The function to authorize an incoming request from Slack
99+
by checking if there is a team/user in the installation data.
91100
:param installation_store: The module offering save/find operations of installation data
92101
:param oauth_settings: The settings related to Slack app installation flow (OAuth flow)
93102
:param oauth_flow: Manually instantiated slack_bolt.oauth.OAuthFlow.
@@ -123,7 +132,18 @@ def __init__(
123132
else:
124133
self._client = create_web_client(token) # NOTE: the token here can be None
125134

135+
self._authorize: Optional[Authorize] = None
136+
if authorize is not None:
137+
self._authorize = CallableAuthorize(
138+
logger=self._framework_logger, func=authorize
139+
)
140+
126141
self._installation_store: Optional[InstallationStore] = installation_store
142+
if self._installation_store is not None and self._authorize is None:
143+
self._authorize = InstallationStoreAuthorize(
144+
installation_store=self._installation_store,
145+
logger=self._framework_logger,
146+
)
127147

128148
self._oauth_flow: Optional[OAuthFlow] = None
129149
if oauth_flow:
@@ -132,6 +152,8 @@ def __init__(
132152
self._installation_store = self._oauth_flow.settings.installation_store
133153
if self._oauth_flow._client is None:
134154
self._oauth_flow._client = self._client
155+
if self._authorize is None:
156+
self._authorize = self._oauth_flow.settings.authorize
135157
elif oauth_settings is not None:
136158
if self._installation_store:
137159
# Consistently use a single installation_store
@@ -140,12 +162,14 @@ def __init__(
140162
self._oauth_flow = OAuthFlow(
141163
client=self.client, logger=self.logger, settings=oauth_settings
142164
)
165+
if self._authorize is None:
166+
self._authorize = self._oauth_flow.settings.authorize
143167

144-
if self._installation_store is not None and self._token is not None:
168+
if (
169+
self._installation_store is not None or self._authorize is not None
170+
) and self._token is not None:
145171
self._token = None
146-
self._framework_logger.warning(
147-
warning_installation_store_prioritized_and_token_skipped()
148-
)
172+
self._framework_logger.warning(warning_token_skipped())
149173

150174
self._middleware_list: List[Union[Callable, Middleware]] = []
151175
self._listeners: List[Listener] = []
@@ -171,19 +195,23 @@ def _init_middleware_list(self):
171195
self._middleware_list.append(RequestVerification(self._signing_secret))
172196

173197
if self._oauth_flow is None:
174-
if self._token:
198+
if self._token is not None:
175199
try:
176200
auth_test_result = self._client.auth_test(token=self._token)
177201
self._middleware_list.append(
178202
SingleTeamAuthorization(auth_test_result=auth_test_result)
179203
)
180204
except SlackApiError as err:
181205
raise BoltError(error_auth_test_failure(err.response))
206+
elif self._authorize is not None:
207+
self._middleware_list.append(
208+
MultiTeamsAuthorization(authorize=self._authorize)
209+
)
182210
else:
183211
raise BoltError(error_token_required())
184212
else:
185213
self._middleware_list.append(
186-
MultiTeamsAuthorization(installation_store=self._installation_store)
214+
MultiTeamsAuthorization(authorize=self._authorize)
187215
)
188216
self._middleware_list.append(IgnoringSelfEvents())
189217
self._middleware_list.append(UrlVerification())

slack_bolt/app/async_app.py

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@
1111
)
1212
from slack_sdk.web.async_client import AsyncWebClient
1313

14+
from slack_bolt.authorization import AuthorizationResult
15+
from slack_bolt.authorization.async_authorize import (
16+
AsyncAuthorize,
17+
AsyncCallableAuthorize,
18+
AsyncInstallationStoreAuthorize,
19+
)
1420
from slack_bolt.context.ack.async_ack import AsyncAck
1521
from slack_bolt.error import BoltError
1622
from slack_bolt.logger.messages import (
1723
error_signing_secret_not_found,
1824
warning_client_prioritized_and_token_skipped,
19-
warning_installation_store_prioritized_and_token_skipped,
25+
warning_token_skipped,
2026
error_token_required,
2127
warning_unhandled_request,
2228
debug_checking_listener,
@@ -82,6 +88,7 @@ def __init__(
8288
client: Optional[AsyncWebClient] = None,
8389
# for multi-workspace apps
8490
installation_store: Optional[AsyncInstallationStore] = None,
91+
authorize: Optional[Callable[..., Awaitable[AuthorizationResult]]] = None,
8592
# for the OAuth flow
8693
oauth_settings: Optional[AsyncOAuthSettings] = None,
8794
oauth_flow: Optional[AsyncOAuthFlow] = None,
@@ -97,6 +104,8 @@ def __init__(
97104
:param token: The bot access token required only for single-workspace app.
98105
:param client: The singleton slack_sdk.web.async_client.AsyncWebClient instance for this app.
99106
:param installation_store: The module offering save/find operations of installation data
107+
:param authorize: The function to authorize an incoming request from Slack
108+
by checking if there is a team/user in the installation data.
100109
:param oauth_settings: The settings related to Slack app installation flow (OAuth flow)
101110
:param oauth_flow: Manually instantiated slack_bolt.oauth.async_oauth_flow.AsyncOAuthFlow.
102111
This is always prioritized over oauth_settings.
@@ -131,9 +140,20 @@ def __init__(
131140
# NOTE: the token here can be None
132141
self._async_client = create_async_web_client(token)
133142

143+
self._async_authorize: Optional[AsyncAuthorize] = None
144+
if authorize is not None:
145+
self._async_authorize = AsyncCallableAuthorize(
146+
logger=self._framework_logger, func=authorize
147+
)
148+
134149
self._async_installation_store: Optional[
135150
AsyncInstallationStore
136151
] = installation_store
152+
if self._async_installation_store is not None and self._async_authorize is None:
153+
self._async_authorize = AsyncInstallationStoreAuthorize(
154+
installation_store=self._async_installation_store,
155+
logger=self._framework_logger,
156+
)
137157

138158
self._async_oauth_flow: Optional[AsyncOAuthFlow] = None
139159
if oauth_flow:
@@ -144,6 +164,8 @@ def __init__(
144164
)
145165
if self._async_oauth_flow._async_client is None:
146166
self._async_oauth_flow._async_client = self._async_client
167+
if self._async_authorize is None:
168+
self._async_authorize = self._async_oauth_flow.settings.authorize
147169
elif oauth_settings is not None:
148170
if self._async_installation_store:
149171
# Consistently use a single installation_store
@@ -152,12 +174,15 @@ def __init__(
152174
self._async_oauth_flow = AsyncOAuthFlow(
153175
client=self._async_client, logger=self.logger, settings=oauth_settings
154176
)
177+
if self._async_authorize is None:
178+
self._async_authorize = self._async_oauth_flow.settings.authorize
155179

156-
if self._async_installation_store is not None and self._token is not None:
180+
if (
181+
self._async_installation_store is not None
182+
or self._async_authorize is not None
183+
) and self._token is not None:
157184
self._token = None
158-
self._framework_logger.warning(
159-
warning_installation_store_prioritized_and_token_skipped()
160-
)
185+
self._framework_logger.warning(warning_token_skipped())
161186

162187
self._async_middleware_list: List[Union[Callable, AsyncMiddleware]] = []
163188
self._async_listeners: List[AsyncListener] = []
@@ -184,13 +209,15 @@ def _init_async_middleware_list(self):
184209
if self._async_oauth_flow is None:
185210
if self._token:
186211
self._async_middleware_list.append(AsyncSingleTeamAuthorization())
212+
elif self._async_authorize is not None:
213+
self._async_middleware_list.append(
214+
AsyncMultiTeamsAuthorization(authorize=self._async_authorize)
215+
)
187216
else:
188217
raise BoltError(error_token_required())
189218
else:
190219
self._async_middleware_list.append(
191-
AsyncMultiTeamsAuthorization(
192-
installation_store=self._async_installation_store
193-
)
220+
AsyncMultiTeamsAuthorization(authorize=self._async_authorize)
194221
)
195222

196223
self._async_middleware_list.append(AsyncIgnoringSelfEvents())

slack_bolt/auth/__init__.py

Lines changed: 0 additions & 1 deletion
This file was deleted.

slack_bolt/auth/result.py

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .authorization_result import AuthorizationResult

0 commit comments

Comments
 (0)