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
49 changes: 49 additions & 0 deletions samples/app_authorize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# ------------------------------------------------
# instead of slack_bolt in requirements.txt
import sys


sys.path.insert(1, "..")
# ------------------------------------------------

import logging
logging.basicConfig(level=logging.DEBUG)

import os
from slack_bolt import App
from slack_bolt.authorization import AuthorizationResult
from slack_sdk import WebClient

def authorize(enterprise_id, team_id, user_id, client: WebClient, logger):
logger.info(f"{enterprise_id},{team_id},{user_id}")
# You can implement your own logic here
token = os.environ["MY_TOKEN"]
return AuthorizationResult.from_auth_test_response(
auth_test_response=client.auth_test(token=token),
bot_token=token,
)

app = App(
signing_secret=os.environ["SLACK_SIGNING_SECRET"],
authorize=authorize
)

@app.command("/hello-bolt-python")
def hello_command(ack, body):
user_id = body["user_id"]
ack(f"Hi <@{user_id}>!")


@app.event("app_mention")
def event_test(body, say, logger):
logger.info(body)
say("What's up?")


if __name__ == "__main__":
app.start(3000)

# pip install slack_bolt
# export SLACK_SIGNING_SECRET=***
# export MY_TOKEN=xoxb-***
# python app.py
51 changes: 51 additions & 0 deletions samples/async_app_authorize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# ------------------------------------------------
# instead of slack_bolt in requirements.txt
import sys

sys.path.insert(1, "..")
# ------------------------------------------------

import logging

logging.basicConfig(level=logging.DEBUG)

import os
from slack_sdk.web.async_client import AsyncWebClient
from slack_bolt.authorization import AuthorizationResult
from slack_bolt.async_app import AsyncApp

async def authorize(enterprise_id, team_id, user_id, client: AsyncWebClient, logger):
logger.info(f"{enterprise_id},{team_id},{user_id}")
# You can implement your own logic here
token = os.environ["MY_TOKEN"]
return AuthorizationResult.from_auth_test_response(
auth_test_response=await client.auth_test(token=token),
bot_token=token,
)


app = AsyncApp(
signing_secret=os.environ["SLACK_SIGNING_SECRET"],
authorize=authorize
)

@app.event("app_mention")
async def event_test(body, say, logger):
logger.info(body)
await say("What's up?")


@app.command("/hello-bolt-python")
# or app.command(re.compile(r"/hello-.+"))(test_command)
async def command(ack, body):
user_id = body["user_id"]
await ack(f"Hi <@{user_id}>!")


if __name__ == "__main__":
app.start(3000)

# pip install slack_bolt
# export SLACK_SIGNING_SECRET=***
# export MY_TOKEN=xoxb-***
# python app.py
68 changes: 48 additions & 20 deletions slack_bolt/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,30 @@
from slack_sdk.oauth.installation_store import InstallationStore
from slack_sdk.web import WebClient

from slack_bolt.authorization import AuthorizationResult
from slack_bolt.authorization.authorize import (
Authorize,
InstallationStoreAuthorize,
CallableAuthorize,
)
from slack_bolt.error import BoltError
from slack_bolt.lazy_listener.runner import LazyListenerRunner
from slack_bolt.lazy_listener.thread_runner import ThreadLazyListenerRunner
from slack_bolt.listener.custom_listener import CustomListener
from slack_bolt.listener.listener import Listener
from slack_bolt.listener.listener_error_handler import (
ListenerErrorHandler,
DefaultListenerErrorHandler,
CustomListenerErrorHandler,
)
from slack_bolt.listener_matcher import CustomListenerMatcher
from slack_bolt.listener_matcher import builtins as builtin_matchers
from slack_bolt.listener_matcher.listener_matcher import ListenerMatcher
from slack_bolt.logger import get_bolt_app_logger, get_bolt_logger
from slack_bolt.logger.messages import (
error_signing_secret_not_found,
warning_client_prioritized_and_token_skipped,
warning_installation_store_prioritized_and_token_skipped,
warning_token_skipped,
error_auth_test_failure,
error_token_required,
warning_unhandled_request,
Expand All @@ -28,19 +47,6 @@
error_unexpected_listener_middleware,
error_client_invalid_type,
)
from slack_bolt.lazy_listener.runner import LazyListenerRunner
from slack_bolt.lazy_listener.thread_runner import ThreadLazyListenerRunner
from slack_bolt.listener.custom_listener import CustomListener
from slack_bolt.listener.listener import Listener
from slack_bolt.listener.listener_error_handler import (
ListenerErrorHandler,
DefaultListenerErrorHandler,
CustomListenerErrorHandler,
)
from slack_bolt.listener_matcher import CustomListenerMatcher
from slack_bolt.listener_matcher import builtins as builtin_matchers
from slack_bolt.listener_matcher.listener_matcher import ListenerMatcher
from slack_bolt.logger import get_bolt_app_logger, get_bolt_logger
from slack_bolt.middleware import (
Middleware,
SslCheck,
Expand Down Expand Up @@ -73,6 +79,7 @@ def __init__(
token: Optional[str] = None,
client: Optional[WebClient] = None,
# for multi-workspace apps
authorize: Optional[Callable[..., AuthorizationResult]] = None,
installation_store: Optional[InstallationStore] = None,
# for the OAuth flow
oauth_settings: Optional[OAuthSettings] = None,
Expand All @@ -88,6 +95,8 @@ def __init__(
:param signing_secret: The Signing Secret value used for verifying requests from Slack.
:param token: The bot access token required only for single-workspace app.
:param client: The singleton slack_sdk.WebClient instance for this app.
:param authorize: The function to authorize an incoming request from Slack
by checking if there is a team/user in the installation data.
:param installation_store: The module offering save/find operations of installation data
:param oauth_settings: The settings related to Slack app installation flow (OAuth flow)
:param oauth_flow: Manually instantiated slack_bolt.oauth.OAuthFlow.
Expand Down Expand Up @@ -123,7 +132,18 @@ def __init__(
else:
self._client = create_web_client(token) # NOTE: the token here can be None

self._authorize: Optional[Authorize] = None
if authorize is not None:
self._authorize = CallableAuthorize(
logger=self._framework_logger, func=authorize
)

self._installation_store: Optional[InstallationStore] = installation_store
if self._installation_store is not None and self._authorize is None:
self._authorize = InstallationStoreAuthorize(
installation_store=self._installation_store,
logger=self._framework_logger,
)

self._oauth_flow: Optional[OAuthFlow] = None
if oauth_flow:
Expand All @@ -132,6 +152,8 @@ def __init__(
self._installation_store = self._oauth_flow.settings.installation_store
if self._oauth_flow._client is None:
self._oauth_flow._client = self._client
if self._authorize is None:
self._authorize = self._oauth_flow.settings.authorize
elif oauth_settings is not None:
if self._installation_store:
# Consistently use a single installation_store
Expand All @@ -140,12 +162,14 @@ def __init__(
self._oauth_flow = OAuthFlow(
client=self.client, logger=self.logger, settings=oauth_settings
)
if self._authorize is None:
self._authorize = self._oauth_flow.settings.authorize

if self._installation_store is not None and self._token is not None:
if (
self._installation_store is not None or self._authorize is not None
) and self._token is not None:
self._token = None
self._framework_logger.warning(
warning_installation_store_prioritized_and_token_skipped()
)
self._framework_logger.warning(warning_token_skipped())

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

if self._oauth_flow is None:
if self._token:
if self._token is not None:
try:
auth_test_result = self._client.auth_test(token=self._token)
self._middleware_list.append(
SingleTeamAuthorization(auth_test_result=auth_test_result)
)
except SlackApiError as err:
raise BoltError(error_auth_test_failure(err.response))
elif self._authorize is not None:
self._middleware_list.append(
MultiTeamsAuthorization(authorize=self._authorize)
)
else:
raise BoltError(error_token_required())
else:
self._middleware_list.append(
MultiTeamsAuthorization(installation_store=self._installation_store)
MultiTeamsAuthorization(authorize=self._authorize)
)
self._middleware_list.append(IgnoringSelfEvents())
self._middleware_list.append(UrlVerification())
Expand Down
43 changes: 35 additions & 8 deletions slack_bolt/app/async_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,18 @@
)
from slack_sdk.web.async_client import AsyncWebClient

from slack_bolt.authorization import AuthorizationResult
from slack_bolt.authorization.async_authorize import (
AsyncAuthorize,
AsyncCallableAuthorize,
AsyncInstallationStoreAuthorize,
)
from slack_bolt.context.ack.async_ack import AsyncAck
from slack_bolt.error import BoltError
from slack_bolt.logger.messages import (
error_signing_secret_not_found,
warning_client_prioritized_and_token_skipped,
warning_installation_store_prioritized_and_token_skipped,
warning_token_skipped,
error_token_required,
warning_unhandled_request,
debug_checking_listener,
Expand Down Expand Up @@ -82,6 +88,7 @@ def __init__(
client: Optional[AsyncWebClient] = None,
# for multi-workspace apps
installation_store: Optional[AsyncInstallationStore] = None,
authorize: Optional[Callable[..., Awaitable[AuthorizationResult]]] = None,
# for the OAuth flow
oauth_settings: Optional[AsyncOAuthSettings] = None,
oauth_flow: Optional[AsyncOAuthFlow] = None,
Expand All @@ -97,6 +104,8 @@ def __init__(
:param token: The bot access token required only for single-workspace app.
:param client: The singleton slack_sdk.web.async_client.AsyncWebClient instance for this app.
:param installation_store: The module offering save/find operations of installation data
:param authorize: The function to authorize an incoming request from Slack
by checking if there is a team/user in the installation data.
:param oauth_settings: The settings related to Slack app installation flow (OAuth flow)
:param oauth_flow: Manually instantiated slack_bolt.oauth.async_oauth_flow.AsyncOAuthFlow.
This is always prioritized over oauth_settings.
Expand Down Expand Up @@ -131,9 +140,20 @@ def __init__(
# NOTE: the token here can be None
self._async_client = create_async_web_client(token)

self._async_authorize: Optional[AsyncAuthorize] = None
if authorize is not None:
self._async_authorize = AsyncCallableAuthorize(
logger=self._framework_logger, func=authorize
)

self._async_installation_store: Optional[
AsyncInstallationStore
] = installation_store
if self._async_installation_store is not None and self._async_authorize is None:
self._async_authorize = AsyncInstallationStoreAuthorize(
installation_store=self._async_installation_store,
logger=self._framework_logger,
)

self._async_oauth_flow: Optional[AsyncOAuthFlow] = None
if oauth_flow:
Expand All @@ -144,6 +164,8 @@ def __init__(
)
if self._async_oauth_flow._async_client is None:
self._async_oauth_flow._async_client = self._async_client
if self._async_authorize is None:
self._async_authorize = self._async_oauth_flow.settings.authorize
elif oauth_settings is not None:
if self._async_installation_store:
# Consistently use a single installation_store
Expand All @@ -152,12 +174,15 @@ def __init__(
self._async_oauth_flow = AsyncOAuthFlow(
client=self._async_client, logger=self.logger, settings=oauth_settings
)
if self._async_authorize is None:
self._async_authorize = self._async_oauth_flow.settings.authorize

if self._async_installation_store is not None and self._token is not None:
if (
self._async_installation_store is not None
or self._async_authorize is not None
) and self._token is not None:
self._token = None
self._framework_logger.warning(
warning_installation_store_prioritized_and_token_skipped()
)
self._framework_logger.warning(warning_token_skipped())

self._async_middleware_list: List[Union[Callable, AsyncMiddleware]] = []
self._async_listeners: List[AsyncListener] = []
Expand All @@ -184,13 +209,15 @@ def _init_async_middleware_list(self):
if self._async_oauth_flow is None:
if self._token:
self._async_middleware_list.append(AsyncSingleTeamAuthorization())
elif self._async_authorize is not None:
self._async_middleware_list.append(
AsyncMultiTeamsAuthorization(authorize=self._async_authorize)
)
else:
raise BoltError(error_token_required())
else:
self._async_middleware_list.append(
AsyncMultiTeamsAuthorization(
installation_store=self._async_installation_store
)
AsyncMultiTeamsAuthorization(authorize=self._async_authorize)
)

self._async_middleware_list.append(AsyncIgnoringSelfEvents())
Expand Down
1 change: 0 additions & 1 deletion slack_bolt/auth/__init__.py

This file was deleted.

44 changes: 0 additions & 44 deletions slack_bolt/auth/result.py

This file was deleted.

1 change: 1 addition & 0 deletions slack_bolt/authorization/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .authorization_result import AuthorizationResult
Loading