weixin_39702799
weixin_39702799
2020-12-09 01:17

Allow disabling of token auth test

I've written a small app that is running in AWS lambda. I'm using the aws_lambda adapter and it is working great. However, I ran into a problem when attempting to write a test for an unrelated function in my lambda code (using pytest).

I'm setting up the App object in the global scope so that it's available for future function runs that reuse this container. This is in accordance with lambda's best practices. The problem is that I can't import my function code into my test at all unless I have a valid token, which I don't want/need to supply during testing. Here's an example:


import json
import os

import boto3
from slack_bolt import App
from slack_bolt.adapter.aws_lambda import SlackRequestHandler

# Grab secrets for the application.
# First check for local env secrets, otherwise get them from secrets manager
if os.environ.get("AWS_SAM_LOCAL") == "true":
    secrets = {
        "SLACK_SIGNING_SECRET": os.environ.get("OVERRIDE_BOT_TOKEN"),
        "SLACK_BOT_TOKEN": os.environ.get("OVERRIDE_BOT_TOKEN"),
    }
elif os.environ.get("SECRETS_MANAGER_SLACK_SECRET_NAME"):
    session = boto3.session.Session()
    client = session.client(service_name="secretsmanager")
    get_secret_value_response = client.get_secret_value(
        SecretId=os.environ["SECRETS_MANAGER_SLACK_SECRET_NAME"]
    )
    secrets = json.loads(get_secret_value_response["SecretString"])


app = App(
    process_before_response=True,
    signing_secret=secrets["SLACK_SIGNING_SECRET"],
    token=secrets["SLACK_BOT_TOKEN"],
)


.command("/hello-bolt-python-lambda")
def respond_to_slack_within_3_seconds(ack):
    ack("Thanks!")


def lambda_handler(event, context):
    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

Trying to import that file in a test fails because the app can't verify the token. Here's the error:


Traceback (most recent call last):
  File "C:\Users\User\folder\.venv\lib\site-packages\slack_bolt\app\app.py", line 225, in _init_middleware_list
    auth_test_result = self._client.auth_test(token=self._token)
  File "C:\Users\User\folder\.venv\lib\site-packages\slack_sdk\web\client.py", line 756, in auth_test
    return self.api_call("auth.test", json=kwargs)
  File "C:\Users\User\folder\.venv\lib\site-packages\slack_sdk\web\base_client.py", line 121, in api_call
    return self._sync_send(api_url=api_url, req_args=req_args)
  File "C:\Users\User\folder\.venv\lib\site-packages\slack_sdk\web\base_client.py", line 164, in _sync_send
    additional_headers=headers,
  File "C:\Users\User\folder\.venv\lib\site-packages\slack_sdk\web\base_client.py", line 292, in _urllib_api_call
    status_code=response["status"],
  File "C:\Users\User\folder\.venv\lib\site-packages\slack_sdk\web\slack_response.py", line 201, in validate
    raise e.SlackApiError(message=msg, response=self)
slack_sdk.errors.SlackApiError: The request to the Slack API failed.
The server responded with: {'ok': False, 'error': 'invalid_auth'}

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\User\folder\.venv\lib\site-packages\slack_bolt\app\app.py", line 212, in __init__
    self._init_middleware_list()
  File "C:\Users\User\folder\.venv\lib\site-packages\slack_bolt\app\app.py", line 230, in _init_middleware_list
    raise BoltError(error_auth_test_failure(err.response))
slack_bolt.error.BoltError: `token` is invalid (auth.test result: {'ok': False, 'error': 'invalid_auth'})
</module></stdin>

This can be easily reproduced by running: app = App(signing_secret="nope", token="no_token") and observing the same error as above.

I'd like to be able to temporarily disable the token auth test so I can import my code and run a test. I'm not testing any part of slack_bolt, but I've kept all my functions in a single file for simplicity's sake, and I'd like to test one of my other functions.

Category (place an x in each of the [ ])

  • [X] slack_bolt.App and/or its core components
  • [ ] slack_bolt.async_app.AsyncApp and/or its core components
  • [ ] Adapters in slack_bolt.adapter
  • [ ] Others

该提问来源于开源项目:slackapi/bolt-python

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

5条回答

  • weixin_39842955 weixin_39842955 5月前

    Thanks for writing in and sharing the details! Your detailed description helped me understand what the issue you have is.

    I know it's pretty easy to add a new option to turn calling auth.test API off at the timing of initialization. But I would like to hold off adding such options because doing so a lot makes udnerstanding the initialization of App harder for developers.

    So, our recommendation for this use case is to use authorize function with the toekn. Bolt does this under the hood for you. You can learn more about this here: https://slack.dev/bolt-python/concepts#authorization

    python
    def authorize(enterprise_id, team_id, user_id, client: WebClient, logger):
        # If you want to have a cache for this call, you can do so
        token = secrets["SLACK_BOT_TOKEN"]
        return AuthorizeResult.from_auth_test_response(
            auth_test_response=client.auth_test(token=token),
            bot_token=token
        )
    
    
    app = App(
        process_before_response=True,
        signing_secret=secrets["SLACK_SIGNING_SECRET"],
        authorize=authorize
    )
    

    Hope this was helpful to you.

    点赞 评论 复制链接分享
  • weixin_39702799 weixin_39702799 5月前

    I tested your solution and it appears to work fine, thank you. However, it also seems overly complex and very much against the "Zen" of python.

    I also don't agree with the assumption that adding an option to the App initialization makes it harder for developers. If the option is available, and the default is kept the same as it is now, I don't think that makes using App() more complex. I suppose I can see that adding more options, and thus making the App() constructor slightly more complex could make reading it more difficult. But that is a (IMO) small price to pay for added functionality.

    Regardless, thank you for the code snippet that fixed my problem. I appreciate it!

    点赞 评论 复制链接分享
  • weixin_39702799 weixin_39702799 5月前

    Another argument against a required auth test: The slack_sdk itself doesn't require it. You can create a WebClient instance with a bogus token no problem. It has the functionality built in to do an auth test, but it doesn't do it until explicitly asked.

    点赞 评论 复制链接分享
  • weixin_39842955 weixin_39842955 5月前

    The reason why Bolt performs auth.test with its default settings is to construct AuthorizeResult that is used for the framework's functionalities. But, if your app can give sufficient information for AuthorizeResult without calling auth.test API (or storing the result somewhere accessible from your app in advance) to Bolt, calling the API in the app is not required at all.

    Thanks for your comments and feedback. I appreciate it. We may revisit the idea to add an option to skip the initialization but we're not going to take action at this point. Can we close this issue now?

    点赞 评论 复制链接分享
  • weixin_39702799 weixin_39702799 5月前

    Thanks for the quick replies. I think that this can be closed now, yes.

    点赞 评论 复制链接分享

相关推荐