Beispiel #1
0
 def test_is_valid_request_body_as_bytes(self):
     verifier = SignatureVerifier(
         signing_secret=self.signing_secret, clock=MockClock()
     )
     self.assertTrue(
         verifier.is_valid_request(self.body.encode("utf-8"), self.headers)
     )
Beispiel #2
0
 def test_is_valid_request_invalid_body(self):
     verifier = SignatureVerifier(
         signing_secret=self.signing_secret,
         clock=MockClock(),
     )
     modified_body = self.body + "------"
     self.assertFalse(verifier.is_valid_request(modified_body, self.headers))
Beispiel #3
0
async def verify_signature(request: Request) -> bool:
    # リクエストの署名を検証
    # ref: https://api.slack.com/authentication/verifying-requests-from-slack
    verifier = SignatureVerifier(settings.SLACK_SIGNING_SECRET)
    if verifier.is_valid_request(await request.body(), dict(request.headers)):
        return True
    raise HTTPException(HTTPStatus.FORBIDDEN)
Beispiel #4
0
    def test_handle_callback(self):
        oauth_flow = OAuthFlow(
            client=WebClient(base_url=self.mock_api_server_base_url),
            settings=OAuthSettings(
                client_id="111.222",
                client_secret="xxx",
                scopes=["chat:write", "commands"],
                installation_store=FileInstallationStore(),
                state_store=FileOAuthStateStore(expiration_seconds=120),
                success_url="https://www.example.com/completion",
                failure_url="https://www.example.com/failure",
            ),
        )
        state = oauth_flow.issue_new_state(None)
        req = BoltRequest(
            body="",
            query=f"code=foo&state={state}",
            headers={
                "cookie": [f"{oauth_flow.settings.state_cookie_name}={state}"]
            },
        )
        resp = oauth_flow.handle_callback(req)
        assert resp.status == 200
        assert "https://www.example.com/completion" in resp.body

        app = App(signing_secret="signing_secret", oauth_flow=oauth_flow)
        global_shortcut_body = {
            "type": "shortcut",
            "token": "verification_token",
            "action_ts": "111.111",
            "team": {
                "id": "T111",
                "domain": "workspace-domain",
                "enterprise_id": "E111",
                "enterprise_name": "Org Name",
            },
            "user": {
                "id": "W111",
                "username": "******",
                "team_id": "T111"
            },
            "callback_id": "test-shortcut",
            "trigger_id": "111.111.xxxxxx",
        }
        body = f"payload={quote(json.dumps(global_shortcut_body))}"
        timestamp = str(int(time()))
        signature_verifier = SignatureVerifier("signing_secret")
        headers = {
            "content-type": ["application/x-www-form-urlencoded"],
            "x-slack-signature": [
                signature_verifier.generate_signature(body=body,
                                                      timestamp=timestamp)
            ],
            "x-slack-request-timestamp": [timestamp],
        }
        request = BoltRequest(body=body, headers=headers)
        response = app.dispatch(request)
        assert response.status == 200
        assert self.mock_received_requests["/auth.test"] == 1
Beispiel #5
0
    def __init__(self, signing_secret: str):
        """Verifies an incoming request by checking the validity of
        x-slack-signature, x-slack-request-timestamp, and its body data.

        :param signing_secret: The signing secret.
        """
        self.verifier = SignatureVerifier(signing_secret=signing_secret)
        self.logger = get_bolt_logger(RequestVerification)
Beispiel #6
0
 def test_is_valid_request_none(self):
     verifier = SignatureVerifier(
         signing_secret=self.signing_secret,
         clock=MockClock(),
     )
     self.assertFalse(verifier.is_valid_request(None, self.headers))
     self.assertFalse(verifier.is_valid_request(self.body, None))
     self.assertFalse(verifier.is_valid_request(None, None))
Beispiel #7
0
 def test_is_valid(self):
     verifier = SignatureVerifier(
         signing_secret=self.signing_secret,
         clock=MockClock(),
     )
     self.assertTrue(
         verifier.is_valid(self.body, self.timestamp, self.valid_signature)
     )
     self.assertTrue(verifier.is_valid(self.body, 1531420618, self.valid_signature))
    def __init__(self, signing_secret: str):
        """Verifies an incoming request by checking the validity of
        `x-slack-signature`, `x-slack-request-timestamp`, and its body data.

        Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

        Args:
            signing_secret: The signing secret
        """
        self.verifier = SignatureVerifier(signing_secret=signing_secret)
        self.logger = get_bolt_logger(RequestVerification)
Beispiel #9
0
    def wrapper_verify_slack_signature(request, *args, **kwargs):
        signature_verifier = SignatureVerifier(
            signing_secret=settings.SLACK_SIGNING_SECRET)
        if not signature_verifier.is_valid(
                body=request.body,
                timestamp=request.headers.get("X-Slack-Request-Timestamp"),
                signature=request.headers.get("X-Slack-Signature"),
        ):
            return HttpResponseForbidden()

        return func(request, *args, **kwargs)
Beispiel #10
0
def test_verified():
    app.dependency_overrides[verify_signature] = verify_signature
    client = TestClient(app)
    timestamp = int(datetime.now().timestamp())
    data = {'type': 'url_verification', 'token': 'token'}
    verifier = SignatureVerifier(settings.SLACK_SIGNING_SECRET)
    signature = verifier.generate_signature(timestamp=timestamp,
                                            body=json.dumps(data))
    headers = {
        'x-slack-request-timestamp': str(timestamp),
        'x-slack-signature': signature
    }
    res = client.post('/v1/events/', json=data, headers=headers)
    assert res.status_code == HTTPStatus.OK
Beispiel #11
0
 def test_is_valid_none(self):
     verifier = SignatureVerifier(
         signing_secret=self.signing_secret,
         clock=MockClock(),
     )
     self.assertFalse(verifier.is_valid(None, self.timestamp, self.valid_signature))
     self.assertFalse(verifier.is_valid(self.body, None, self.valid_signature))
     self.assertFalse(verifier.is_valid(self.body, self.timestamp, None))
     self.assertFalse(verifier.is_valid(None, None, self.valid_signature))
     self.assertFalse(verifier.is_valid(None, self.timestamp, None))
     self.assertFalse(verifier.is_valid(self.body, None, None))
     self.assertFalse(verifier.is_valid(None, None, None))
Beispiel #12
0
 def execute(self, raw_body, headers):
     is_valid = SignatureVerifier(self.signing_secret).is_valid_request(
         headers=headers, body=raw_body
     )
     if not is_valid:
         logging.warning(f"SignatureVerifier returned {is_valid}")
     return is_valid
Beispiel #13
0
class TestRequestVerification:
    signing_secret = "secret"
    signature_verifier = SignatureVerifier(signing_secret)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body,
            timestamp=timestamp,
        )

    def build_headers(self, timestamp: str, body: str):
        return {
            "content-type": ["application/x-www-form-urlencoded"],
            "x-slack-signature": [self.generate_signature(body, timestamp)],
            "x-slack-request-timestamp": [timestamp],
        }

    def test_valid(self):
        middleware = RequestVerification(signing_secret=self.signing_secret)
        timestamp = str(int(time()))
        raw_body = "payload={}"
        req = BoltRequest(body=raw_body,
                          headers=self.build_headers(timestamp, raw_body))
        resp = BoltResponse(status=404, body="default")
        resp = middleware.process(req=req, resp=resp, next=next)
        assert resp.status == 200
        assert resp.body == "next"

    def test_invalid(self):
        middleware = RequestVerification(signing_secret=self.signing_secret)
        req = BoltRequest(body="payload={}", headers={})
        resp = BoltResponse(status=404)
        resp = middleware.process(req=req, resp=resp, next=next)
        assert resp.status == 401
        assert resp.body == """{"error": "invalid request"}"""
def event_signature_headers(secret, data):
    timestamp = str(int(time()))
    return {
        "X-Slack-Request-Timestamp": str(int(timestamp)),
        "X-Slack-Signature": SignatureVerifier(secret).generate_signature(
            timestamp=timestamp, body=data
        ),
    }
Beispiel #15
0
class TestAsyncInstallationStoreAuthorize:
    signing_secret = "secret"
    mock_api_server_base_url = "http://localhost:8888"
    signature_verifier = SignatureVerifier(signing_secret)
    web_client = AsyncWebClient(
        token=valid_token,
        base_url=mock_api_server_base_url,
    )

    @pytest.fixture
    def event_loop(self):
        old_os_env = remove_os_env_temporarily()
        try:
            setup_mock_web_api_server(self)
            loop = asyncio.get_event_loop()
            yield loop
            loop.close()
            cleanup_mock_web_api_server(self)
        finally:
            restore_os_env(old_os_env)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body,
            timestamp=timestamp,
        )

    def build_headers(self, timestamp: str, body: str):
        return {
            "content-type": ["application/x-www-form-urlencoded"],
            "x-slack-signature": [self.generate_signature(body, timestamp)],
            "x-slack-request-timestamp": [timestamp],
        }

    def build_valid_request(self) -> AsyncBoltRequest:
        timestamp = str(int(time()))
        return AsyncBoltRequest(body=raw_body,
                                headers=self.build_headers(
                                    timestamp, raw_body))

    @pytest.mark.asyncio
    async def test_success(self):
        app = AsyncApp(
            client=self.web_client,
            installation_store=MyInstallationStore(),
            signing_secret=self.signing_secret,
        )
        app.action("a")(simple_listener)

        request = self.build_valid_request()
        response = await app.async_dispatch(request)
        assert response.status == 200
        assert response.body == ""
        await assert_auth_test_count_async(self, 1)
Beispiel #16
0
class TestAsyncSSLCheck:
    signing_secret = "secret"
    valid_token = "xoxb-valid"
    mock_api_server_base_url = "http://localhost:8888"
    signature_verifier = SignatureVerifier(signing_secret)
    web_client = AsyncWebClient(
        token=valid_token,
        base_url=mock_api_server_base_url,
    )

    @pytest.fixture
    def event_loop(self):
        old_os_env = remove_os_env_temporarily()
        try:
            setup_mock_web_api_server(self)
            loop = asyncio.get_event_loop()
            yield loop
            loop.close()
            cleanup_mock_web_api_server(self)
        finally:
            restore_os_env(old_os_env)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body,
            timestamp=timestamp,
        )

    @pytest.mark.asyncio
    async def test_mock_server_is_running(self):
        resp = await self.web_client.api_test()
        assert resp != None

    @pytest.mark.asyncio
    async def test_ssl_check(self):
        app = AsyncApp(client=self.web_client,
                       signing_secret=self.signing_secret)

        timestamp, body = str(int(time())), "token=random&ssl_check=1"
        request: AsyncBoltRequest = AsyncBoltRequest(
            body=body,
            query={},
            headers={
                "content-type": ["application/x-www-form-urlencoded"],
                "x-slack-signature":
                [self.generate_signature(body, timestamp)],
                "x-slack-request-timestamp": [timestamp],
            },
        )
        response = await app.async_dispatch(request)
        assert response.status == 200
        assert response.body == ""
class TestInstallationStoreAuthorize:
    signing_secret = "secret"
    mock_api_server_base_url = "http://localhost:8888"
    signature_verifier = SignatureVerifier(signing_secret)
    web_client = WebClient(
        token=valid_token,
        base_url=mock_api_server_base_url,
    )

    def setup_method(self):
        self.old_os_env = remove_os_env_temporarily()
        setup_mock_web_api_server(self)

    def teardown_method(self):
        cleanup_mock_web_api_server(self)
        restore_os_env(self.old_os_env)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body,
            timestamp=timestamp,
        )

    def build_headers(self, timestamp: str, body: str):
        return {
            "content-type": ["application/x-www-form-urlencoded"],
            "x-slack-signature": [self.generate_signature(body, timestamp)],
            "x-slack-request-timestamp": [timestamp],
        }

    def build_valid_request(self) -> BoltRequest:
        timestamp = str(int(time()))
        return BoltRequest(
            body=raw_body, headers=self.build_headers(timestamp, raw_body)
        )

    def test_success(self):
        app = App(
            client=self.web_client,
            installation_store=MyInstallationStore(),
            signing_secret=self.signing_secret,
        )
        app.action("a")(simple_listener)

        request = self.build_valid_request()
        response = app.dispatch(request)
        assert response.status == 200
        assert response.body == ""
        assert_auth_test_count(self, 1)
class RequestVerification(Middleware):  # type: ignore
    def __init__(self, signing_secret: str):
        """Verifies an incoming request by checking the validity of
        `x-slack-signature`, `x-slack-request-timestamp`, and its body data.

        Refer to https://api.slack.com/authentication/verifying-requests-from-slack for details.

        Args:
            signing_secret: The signing secret
        """
        self.verifier = SignatureVerifier(signing_secret=signing_secret)
        self.logger = get_bolt_logger(RequestVerification)

    def process(
        self,
        *,
        req: BoltRequest,
        resp: BoltResponse,
        next: Callable[[], BoltResponse],
    ) -> BoltResponse:
        if self._can_skip(req.mode, req.body):
            return next()

        body = req.raw_body
        timestamp = req.headers.get("x-slack-request-timestamp", ["0"])[0]
        signature = req.headers.get("x-slack-signature", [""])[0]
        if self.verifier.is_valid(body, timestamp, signature):
            return next()
        else:
            self._debug_log_error(signature, timestamp, body)
            return self._build_error_response()

    # -----------------------------------------

    @staticmethod
    def _can_skip(mode: str, body: Dict[str, Any]) -> bool:
        return mode == "socket_mode" or (body is not None
                                         and body.get("ssl_check") == "1")

    @staticmethod
    def _build_error_response() -> BoltResponse:
        return BoltResponse(status=401, body={"error": "invalid request"})

    def _debug_log_error(self, signature, timestamp, body) -> None:
        self.logger.info(
            "Invalid request signature detected "
            f"(signature: {signature}, timestamp: {timestamp}, body: {body})")
Beispiel #19
0
class TestSSLCheck:
    signing_secret = "secret"
    valid_token = "xoxb-valid"
    mock_api_server_base_url = "http://localhost:8888"
    signature_verifier = SignatureVerifier(signing_secret)
    web_client = WebClient(
        token=valid_token,
        base_url=mock_api_server_base_url,
    )

    def setup_method(self):
        self.old_os_env = remove_os_env_temporarily()
        setup_mock_web_api_server(self)

    def teardown_method(self):
        cleanup_mock_web_api_server(self)
        restore_os_env(self.old_os_env)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body,
            timestamp=timestamp,
        )

    def test_mock_server_is_running(self):
        resp = self.web_client.api_test()
        assert resp != None

    def test_ssl_check(self):
        app = App(client=self.web_client, signing_secret=self.signing_secret)

        timestamp, body = str(int(time())), "token=random&ssl_check=1"
        request: BoltRequest = BoltRequest(
            body=body,
            query={},
            headers={
                "content-type": ["application/x-www-form-urlencoded"],
                "x-slack-signature":
                [self.generate_signature(body, timestamp)],
                "x-slack-request-timestamp": [timestamp],
            },
        )
        response = app.dispatch(request)
        assert response.status == 200
        assert response.body == ""
class TestCherryPy(helper.CPWebCase):
    helper.CPWebCase.interactive = False
    signing_secret = "secret"
    signature_verifier = SignatureVerifier(signing_secret)

    @classmethod
    def setup_server(cls):
        cls.old_os_env = remove_os_env_temporarily()

        signing_secret = "secret"
        valid_token = "xoxb-valid"
        mock_api_server_base_url = "http://localhost:8888"
        web_client = WebClient(
            token=valid_token,
            base_url=mock_api_server_base_url,
        )
        app = App(
            client=web_client,
            signing_secret=signing_secret,
            oauth_settings=OAuthSettings(
                client_id="111.111",
                client_secret="xxx",
                scopes=["chat:write", "commands"],
            ),
        )
        handler = SlackRequestHandler(app)

        class SlackApp(object):
            @cherrypy.expose
            @cherrypy.tools.slack_in()
            def install(self, **kwargs):
                return handler.handle()

        cherrypy.tree.mount(SlackApp(), "/slack")

    @classmethod
    def teardown_class(cls):
        cls.supervisor.stop()
        restore_os_env(cls.old_os_env)

    def test_oauth(self):
        cherrypy.request.process_request_body = False
        self.getPage("/slack/install", method="GET")
        self.assertStatus("200 OK")
Beispiel #21
0
class TestAsyncRequestVerification:
    signing_secret = "secret"
    signature_verifier = SignatureVerifier(signing_secret)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body,
            timestamp=timestamp,
        )

    def build_headers(self, timestamp: str, body: str):
        return {
            "content-type": ["application/x-www-form-urlencoded"],
            "x-slack-signature": [self.generate_signature(body, timestamp)],
            "x-slack-request-timestamp": [timestamp],
        }

    @pytest.fixture
    def event_loop(self):
        loop = asyncio.get_event_loop()
        yield loop
        loop.close()

    @pytest.mark.asyncio
    async def test_valid(self):
        middleware = AsyncRequestVerification(signing_secret="secret")
        timestamp = str(int(time()))
        raw_body = "payload={}"
        req = AsyncBoltRequest(body=raw_body,
                               headers=self.build_headers(timestamp, raw_body))
        resp = BoltResponse(status=404)
        resp = await middleware.async_process(req=req, resp=resp, next=next)
        assert resp.status == 200
        assert resp.body == "next"

    @pytest.mark.asyncio
    async def test_invalid(self):
        middleware = AsyncRequestVerification(signing_secret="secret")
        req = AsyncBoltRequest(body="payload={}", headers={})
        resp = BoltResponse(status=404)
        resp = await middleware.async_process(req=req, resp=resp, next=next)
        assert resp.status == 401
        assert resp.body == """{"error": "invalid request"}"""
class RequestVerification(Middleware):  # type: ignore
    def __init__(self, signing_secret: str):
        self.verifier = SignatureVerifier(signing_secret=signing_secret)
        self.logger = get_bolt_logger(RequestVerification)

    def process(
        self,
        *,
        req: BoltRequest,
        resp: BoltResponse,
        next: Callable[[], BoltResponse],
    ) -> BoltResponse:
        if self._can_skip(req.payload):
            return next()

        body = req.body
        timestamp = req.headers.get("x-slack-request-timestamp", ["0"])[0]
        signature = req.headers.get("x-slack-signature", [""])[0]
        if self.verifier.is_valid(body, timestamp, signature):
            return next()
        else:
            self._debug_log_error(signature, timestamp, body)
            return self._build_error_response()

    # -----------------------------------------

    @staticmethod
    def _can_skip(payload: Dict[str, Any]) -> bool:
        return payload is not None and payload.get("ssl_check", None) == "1"

    @staticmethod
    def _build_error_response() -> BoltResponse:
        return BoltResponse(status=401, body={"error": "invalid request"})

    def _debug_log_error(self, signature, timestamp, body) -> None:
        self.logger.info(
            "Invalid request signature detected "
            f"(signature: {signature}, timestamp: {timestamp}, body: {body})")
Beispiel #23
0
class TestEventsSharedChannels:
    signing_secret = "secret"
    mock_api_server_base_url = "http://*****:*****@W111> Hi there!",
            "user": "******",
            "ts": "1595926230.009600",
            "team": "T_INSTALLED",
            "channel": "C111",
            "event_ts": "1595926230.009600",
        },
        "type": "event_callback",
        "event_id": "Ev111",
        "event_time": 1595926230,
        "authorizations": [
            {
                "enterprise_id": "E_INSTALLED",
                "team_id": "T_INSTALLED",
                "user_id": "W111",
                "is_bot": True,
                "is_enterprise_install": False,
            }
        ],
    }

    def test_mock_server_is_running(self):
        resp = self.web_client.api_test(token=valid_token)
        assert resp != None

    def test_middleware(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            authorize=authorize,
        )

        @app.event("app_mention")
        def handle_app_mention(body, say: Say, payload, event):
            assert body == self.valid_event_body
            assert body["event"] == payload
            assert payload == event
            say("What's up?")

        timestamp, body = str(int(time())), json.dumps(self.valid_event_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1

    def test_middleware_skip(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            authorize=authorize,
        )

        def skip_middleware(req, resp, next):
            # return next()
            pass

        @app.event("app_mention", middleware=[skip_middleware])
        def handle_app_mention(body, logger, payload, event):
            assert body["event"] == payload
            assert payload == event
            logger.info(payload)

        timestamp, body = str(int(time())), json.dumps(self.valid_event_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 404
        assert_auth_test_count(self, 1)

    valid_reaction_added_body = {
        "token": "verification_token",
        "team_id": "T_SOURCE",
        "enterprise_id": "E_SOURCE",
        "api_app_id": "A111",
        "event": {
            "type": "reaction_added",
            "user": "******",
            "item": {"type": "message", "channel": "C111", "ts": "1599529504.000400"},
            "reaction": "heart_eyes",
            "item_user": "******",
            "event_ts": "1599616881.000800",
        },
        "type": "event_callback",
        "event_id": "Ev111",
        "event_time": 1599616881,
        "authorizations": [
            {
                "enterprise_id": "E_INSTALLED",
                "team_id": "T_INSTALLED",
                "user_id": "W111",
                "is_bot": True,
                "is_enterprise_install": False,
            }
        ],
    }

    def test_reaction_added(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            authorize=authorize,
        )

        @app.event("reaction_added")
        def handle_app_mention(body, say, payload, event):
            assert body == self.valid_reaction_added_body
            assert body["event"] == payload
            assert payload == event
            say("What's up?")

        timestamp, body = str(int(time())), json.dumps(self.valid_reaction_added_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1

    def test_stable_auto_ack(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            authorize=authorize,
        )

        @app.event("reaction_added")
        def handle_app_mention():
            raise Exception("Something wrong!")

        for _ in range(10):
            timestamp, body = (
                str(int(time())),
                json.dumps(self.valid_reaction_added_body),
            )
            request: BoltRequest = BoltRequest(
                body=body, headers=self.build_headers(timestamp, body)
            )
            response = app.dispatch(request)
            assert response.status == 200

    def test_self_events(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            authorize=authorize,
        )

        event_body = {
            "token": "verification_token",
            "team_id": "T_SOURCE",
            "enterprise_id": "E_SOURCE",
            "api_app_id": "A111",
            "event": {
                "type": "reaction_added",
                "user": "******",  # bot_user_id
                "item": {
                    "type": "message",
                    "channel": "C111",
                    "ts": "1599529504.000400",
                },
                "reaction": "heart_eyes",
                "item_user": "******",
                "event_ts": "1599616881.000800",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1599616881,
            "authorizations": [
                {
                    "enterprise_id": "E_INSTALLED",
                    "team_id": "T_INSTALLED",
                    "user_id": "W111",
                    "is_bot": True,
                    "is_enterprise_install": False,
                }
            ],
        }

        @app.event("reaction_added")
        def handle_app_mention(say):
            say("What's up?")

        timestamp, body = str(int(time())), json.dumps(event_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        # The listener should not be executed
        assert self.mock_received_requests.get("/chat.postMessage") is None

    def test_self_member_join_left_events(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            authorize=authorize,
        )

        join_event_body = {
            "token": "verification_token",
            "team_id": "T_SOURCE",
            "enterprise_id": "E_SOURCE",
            "api_app_id": "A111",
            "event": {
                "type": "member_joined_channel",
                "user": "******",  # bot_user_id
                "channel": "C111",
                "channel_type": "C",
                "team": "T_INSTALLED",
                "inviter": "U222",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1599616881,
            "authorizations": [
                {
                    "enterprise_id": "E_INSTALLED",
                    "team_id": "T_INSTALLED",
                    "user_id": "W111",
                    "is_bot": True,
                    "is_enterprise_install": False,
                }
            ],
        }

        left_event_body = {
            "token": "verification_token",
            "team_id": "T_SOURCE",
            "enterprise_id": "E_SOURCE",
            "api_app_id": "A111",
            "event": {
                "type": "member_left_channel",
                "user": "******",  # bot_user_id
                "channel": "C111",
                "channel_type": "C",
                "team": "T_INSTALLED",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1599616881,
            "authorizations": [
                {
                    "enterprise_id": "E_INSTALLED",
                    "team_id": "T_INSTALLED",
                    "user_id": "W111",
                    "is_bot": True,
                    "is_enterprise_install": False,
                }
            ],
        }

        @app.event("member_joined_channel")
        def handle_member_joined_channel(say):
            say("What's up?")

        @app.event("member_left_channel")
        def handle_member_left_channel(say):
            say("What's up?")

        timestamp, body = str(int(time())), json.dumps(join_event_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 200
        assert_auth_test_count(self, 1)

        timestamp, body = str(int(time())), json.dumps(left_event_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 200

        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 2

    def test_member_join_left_events(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            authorize=authorize,
        )

        join_event_body = {
            "token": "verification_token",
            "team_id": "T_SOURCE",
            "enterprise_id": "E_SOURCE",
            "api_app_id": "A111",
            "event": {
                "type": "member_joined_channel",
                "user": "******",  # not self
                "channel": "C111",
                "channel_type": "C",
                "team": "T_INSTALLED",
                "inviter": "U222",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1599616881,
            "authorizations": [
                {
                    "enterprise_id": "E_INSTALLED",
                    "team_id": "T_INSTALLED",
                    "user_id": "W111",
                    "is_bot": True,
                    "is_enterprise_install": False,
                }
            ],
        }

        left_event_body = {
            "token": "verification_token",
            "team_id": "T_SOURCE",
            "enterprise_id": "E_SOURCE",
            "api_app_id": "A111",
            "event": {
                "type": "member_left_channel",
                "user": "******",  # not self
                "channel": "C111",
                "channel_type": "C",
                "team": "T_INSTALLED",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1599616881,
            "authorizations": [
                {
                    "enterprise_id": "E_INSTALLED",
                    "team_id": "T_INSTALLED",
                    "user_id": "W111",
                    "is_bot": True,
                    "is_enterprise_install": False,
                }
            ],
        }

        @app.event("member_joined_channel")
        def handle_app_mention(say):
            say("What's up?")

        @app.event("member_left_channel")
        def handle_app_mention(say):
            say("What's up?")

        timestamp, body = str(int(time())), json.dumps(join_event_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 200
        assert_auth_test_count(self, 1)

        timestamp, body = str(int(time())), json.dumps(left_event_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 200

        sleep(1)  # wait a bit after auto ack()
        # the listeners should not be executed
        assert self.mock_received_requests["/chat.postMessage"] == 2

    def test_uninstallation_and_revokes(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            authorize=authorize,
        )
        app._client = WebClient(
            token="uninstalled-revoked", base_url=self.mock_api_server_base_url
        )

        @app.event("app_uninstalled")
        def handler1(say: Say):
            say(channel="C111", text="What's up?")

        @app.event("tokens_revoked")
        def handler2(say: Say):
            say(channel="C111", text="What's up?")

        app_uninstalled_body = {
            "token": "verification_token",
            "team_id": "T_INSTALLED",
            "enterprise_id": "E_INSTALLED",
            "api_app_id": "A111",
            "event": {"type": "app_uninstalled"},
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1599616881,
            "authorizations": [
                {
                    "enterprise_id": "E_INSTALLED",
                    "team_id": "T_INSTALLED",
                    "user_id": "W111",
                    "is_bot": True,
                    "is_enterprise_install": False,
                }
            ],
        }

        timestamp, body = str(int(time())), json.dumps(app_uninstalled_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 200

        tokens_revoked_body = {
            "token": "verification_token",
            "team_id": "T_INSTALLED",
            "enterprise_id": "E_INSTALLED",
            "api_app_id": "A111",
            "event": {
                "type": "tokens_revoked",
                "tokens": {"oauth": ["UXXXXXXXX"], "bot": ["UXXXXXXXX"]},
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1599616881,
            "authorizations": [
                {
                    "enterprise_id": "E_INSTALLED",
                    "team_id": "T_INSTALLED",
                    "user_id": "W111",
                    "is_bot": True,
                    "is_enterprise_install": False,
                }
            ],
        }

        timestamp, body = str(int(time())), json.dumps(tokens_revoked_body)
        request: BoltRequest = BoltRequest(
            body=body, headers=self.build_headers(timestamp, body)
        )
        response = app.dispatch(request)
        assert response.status == 200

        # this should not be called when we have authorize
        assert self.mock_received_requests.get("/auth.test") is None
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 2
Beispiel #24
0
class TestAsyncOrgApps:
    signing_secret = "secret"
    valid_token = "xoxb-valid"
    mock_api_server_base_url = "http://localhost:8888"
    signature_verifier = SignatureVerifier(signing_secret)
    web_client = AsyncWebClient(token=None, base_url=mock_api_server_base_url)

    @pytest.fixture
    def event_loop(self):
        old_os_env = remove_os_env_temporarily()
        try:
            setup_mock_web_api_server(self)
            loop = asyncio.get_event_loop()
            yield loop
            loop.close()
            cleanup_mock_web_api_server(self)
        finally:
            restore_os_env(old_os_env)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body,
            timestamp=timestamp,
        )

    def build_headers(self, timestamp: str, body: str):
        return {
            "content-type": ["application/json"],
            "x-slack-signature": [self.generate_signature(body, timestamp)],
            "x-slack-request-timestamp": [timestamp],
        }

    @pytest.mark.asyncio
    async def test_team_access_granted(self):
        app = AsyncApp(
            client=self.web_client,
            signing_secret=self.signing_secret,
            installation_store=OrgAppInstallationStore(),
        )

        event_payload = {
            "token": "verification-token",
            "enterprise_id": "E111",
            "api_app_id": "A111",
            "event": {
                "type": "team_access_granted",
                "team_ids": ["T111", "T222"],
                "event_ts": "111.222",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1606805974,
        }

        result = Result()

        @app.event("team_access_granted")
        async def handle_app_mention(body):
            assert body == event_payload
            result.called = True

        timestamp, body = str(int(time())), json.dumps(event_payload)
        request: AsyncBoltRequest = AsyncBoltRequest(
            body=body, headers=self.build_headers(timestamp, body))
        response = await app.async_dispatch(request)
        assert response.status == 200
        # auth.test API call must be skipped
        assert self.mock_received_requests["/auth.test"] == 1
        await asyncio.sleep(1)  # wait a bit after auto ack()
        assert result.called is True

    @pytest.mark.asyncio
    async def test_team_access_revoked(self):
        app = AsyncApp(
            client=self.web_client,
            signing_secret=self.signing_secret,
            installation_store=OrgAppInstallationStore(),
        )

        event_payload = {
            "token": "verification-token",
            "enterprise_id": "E111",
            "api_app_id": "A111",
            "event": {
                "type": "team_access_revoked",
                "team_ids": ["T111", "T222"],
                "event_ts": "1606805732.987656",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1606805732,
        }

        result = Result()

        @app.event("team_access_revoked")
        async def handle_app_mention(body):
            assert body == event_payload
            result.called = True

        timestamp, body = str(int(time())), json.dumps(event_payload)
        request: AsyncBoltRequest = AsyncBoltRequest(
            body=body, headers=self.build_headers(timestamp, body))
        response = await app.async_dispatch(request)
        assert response.status == 200
        # auth.test API call must be skipped
        assert self.mock_received_requests.get("/auth.test") is None
        await asyncio.sleep(1)  # wait a bit after auto ack()
        assert result.called is True

    @pytest.mark.asyncio
    async def test_app_home_opened(self):
        app = AsyncApp(
            client=self.web_client,
            signing_secret=self.signing_secret,
            installation_store=OrgAppInstallationStore(),
        )

        event_payload = {
            "token":
            "verification-token",
            "team_id":
            "T111",
            "enterprise_id":
            "E111",
            "api_app_id":
            "A111",
            "event": {
                "type": "app_home_opened",
                "user": "******",
                "channel": "D111",
                "tab": "messages",
                "event_ts": "1606810927.510671",
            },
            "type":
            "event_callback",
            "event_id":
            "Ev111",
            "event_time":
            1606810927,
            "authorizations": [{
                "enterprise_id": "E111",
                "team_id": None,
                "user_id": "W111",
                "is_bot": True,
                "is_enterprise_install": True,
            }],
            "is_ext_shared_channel":
            False,
        }

        result = Result()

        @app.event("app_home_opened")
        async def handle_app_mention(body):
            assert body == event_payload
            result.called = True

        timestamp, body = str(int(time())), json.dumps(event_payload)
        request: AsyncBoltRequest = AsyncBoltRequest(
            body=body, headers=self.build_headers(timestamp, body))
        response = await app.async_dispatch(request)
        assert response.status == 200
        # auth.test API call must be skipped
        assert self.mock_received_requests["/auth.test"] == 1
        await asyncio.sleep(1)  # wait a bit after auto ack()
        assert result.called is True

    @pytest.mark.asyncio
    async def test_message(self):
        app = AsyncApp(
            client=self.web_client,
            signing_secret=self.signing_secret,
            installation_store=OrgAppInstallationStore(),
        )

        event_payload = {
            "token":
            "verification-token",
            "team_id":
            "T111",
            "enterprise_id":
            "E111",
            "api_app_id":
            "A111",
            "event": {
                "client_msg_id": "0186b75a-2ad4-4f36-8ccc-18608b0ac5d1",
                "type": "message",
                "text": "<@W222>",
                "user": "******",
                "ts": "1606810819.000800",
                "team": "T111",
                "channel": "C111",
                "event_ts": "1606810819.000800",
                "channel_type": "channel",
            },
            "type":
            "event_callback",
            "event_id":
            "Ev111",
            "event_time":
            1606810819,
            "authed_users": [],
            "authorizations": [{
                "enterprise_id": "E111",
                "team_id": None,
                "user_id": "W222",
                "is_bot": True,
                "is_enterprise_install": True,
            }],
            "is_ext_shared_channel":
            False,
            "event_context":
            "1-message-T111-C111",
        }

        result = Result()

        @app.event("message")
        async def handle_app_mention(body):
            assert body == event_payload
            result.called = True

        timestamp, body = str(int(time())), json.dumps(event_payload)
        request: AsyncBoltRequest = AsyncBoltRequest(
            body=body, headers=self.build_headers(timestamp, body))
        response = await app.async_dispatch(request)
        assert response.status == 200
        # auth.test API call must be skipped
        assert self.mock_received_requests["/auth.test"] == 1
        await asyncio.sleep(1)  # wait a bit after auto ack()
        assert result.called is True
Beispiel #25
0
class TestCherryPy(helper.CPWebCase):
    helper.CPWebCase.interactive = False
    signing_secret = "secret"
    signature_verifier = SignatureVerifier(signing_secret)

    @classmethod
    def setup_server(cls):
        cls.old_os_env = remove_os_env_temporarily()
        setup_mock_web_api_server(cls)

        signing_secret = "secret"
        valid_token = "xoxb-valid"
        mock_api_server_base_url = "http://localhost:8888"
        web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url,)
        app = App(client=web_client, signing_secret=signing_secret,)

        def event_handler():
            pass

        def shortcut_handler(ack):
            ack()

        def command_handler(ack):
            ack()

        app.event("app_mention")(event_handler)
        app.shortcut("test-shortcut")(shortcut_handler)
        app.command("/hello-world")(command_handler)

        handler = SlackRequestHandler(app)

        class SlackApp(object):
            @cherrypy.expose
            @cherrypy.tools.slack_in()
            def events(self, **kwargs):
                return handler.handle()

        cherrypy.tree.mount(SlackApp(), "/slack")

    @classmethod
    def teardown_class(cls):
        cls.supervisor.stop()
        cleanup_mock_web_api_server(cls)
        restore_os_env(cls.old_os_env)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body, timestamp=timestamp,
        )

    def build_headers(self, timestamp: str, body: str):
        return [
            ("content-length", str(len(body))),
            ("x-slack-signature", self.generate_signature(body, timestamp)),
            ("x-slack-request-timestamp", timestamp),
        ]

    def test_events(self):
        input = {
            "token": "verification_token",
            "team_id": "T111",
            "enterprise_id": "E111",
            "api_app_id": "A111",
            "event": {
                "client_msg_id": "9cbd4c5b-7ddf-4ede-b479-ad21fca66d63",
                "type": "app_mention",
                "text": "<@W111> Hi there!",
                "user": "******",
                "ts": "1595926230.009600",
                "team": "T111",
                "channel": "C111",
                "event_ts": "1595926230.009600",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1595926230,
            "authed_users": ["W111"],
        }
        timestamp, body = str(int(time())), json.dumps(input)
        cherrypy.request.process_request_body = True
        self.getPage(
            "/slack/events",
            method="POST",
            body=body,
            headers=self.build_headers(timestamp, body),
        )
        self.assertStatus("200 OK")
        self.assertBody("")

    def test_shortcuts(self):
        input = {
            "type": "shortcut",
            "token": "verification_token",
            "action_ts": "111.111",
            "team": {
                "id": "T111",
                "domain": "workspace-domain",
                "enterprise_id": "E111",
                "enterprise_name": "Org Name",
            },
            "user": {"id": "W111", "username": "******", "team_id": "T111"},
            "callback_id": "test-shortcut",
            "trigger_id": "111.111.xxxxxx",
        }

        timestamp, body = str(int(time())), f"payload={quote(json.dumps(input))}"
        cherrypy.request.process_request_body = True
        self.getPage(
            "/slack/events",
            method="POST",
            body=body,
            headers=self.build_headers(timestamp, body),
        )
        self.assertStatus("200 OK")
        self.assertBody("")

    def test_commands(self):
        input = (
            "token=verification_token"
            "&team_id=T111"
            "&team_domain=test-domain"
            "&channel_id=C111"
            "&channel_name=random"
            "&user_id=W111"
            "&user_name=primary-owner"
            "&command=%2Fhello-world"
            "&text=Hi"
            "&enterprise_id=E111"
            "&enterprise_name=Org+Name"
            "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx"
            "&trigger_id=111.111.xxx"
        )
        timestamp, body = str(int(time())), input
        cherrypy.request.process_request_body = True
        self.getPage(
            "/slack/events",
            method="POST",
            body=body,
            headers=self.build_headers(timestamp, body),
        )
        self.assertStatus("200 OK")
        self.assertBody("")
Beispiel #26
0
class TestApp:
    signing_secret = "secret"
    valid_token = "xoxb-valid"
    mock_api_server_base_url = "http://*****:*****@W111> Hi there!",
            "user": "******",
            "ts": "1595926230.009600",
            "team": "T111",
            "channel": "C111",
            "event_ts": "1595926230.009600",
        },
        "type": "event_callback",
        "event_id": "Ev111",
        "event_time": 1595926230,
        "authed_users": ["W111"],
    }

    @staticmethod
    def handle_app_mention(body, say: Say, payload, event):
        assert body["event"] == payload
        assert payload == event
        say("What's up?")

    oauth_settings_bot_only = OAuthSettings(
        client_id="111.222",
        client_secret="valid",
        installation_store=BotOnlyMemoryInstallationStore(),
        installation_store_bot_only=True,
        state_store=FileOAuthStateStore(expiration_seconds=120),
    )

    oauth_settings = OAuthSettings(
        client_id="111.222",
        client_secret="valid",
        installation_store=BotOnlyMemoryInstallationStore(),
        installation_store_bot_only=False,
        state_store=FileOAuthStateStore(expiration_seconds=120),
    )

    def build_app_mention_request(self):
        timestamp, body = str(int(time())), json.dumps(
            self.app_mention_request_body)
        return BoltRequest(body=body,
                           headers=self.build_headers(timestamp, body))

    def test_installation_store_bot_only_default(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            installation_store=MemoryInstallationStore(),
        )

        app.event("app_mention")(self.handle_app_mention)
        response = app.dispatch(self.build_app_mention_request())
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1

    def test_installation_store_bot_only_false(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            installation_store=MemoryInstallationStore(),
            # the default is False
            installation_store_bot_only=False,
        )

        app.event("app_mention")(self.handle_app_mention)
        response = app.dispatch(self.build_app_mention_request())
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1

    def test_installation_store_bot_only(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            installation_store=BotOnlyMemoryInstallationStore(),
            installation_store_bot_only=True,
        )

        app.event("app_mention")(self.handle_app_mention)
        response = app.dispatch(self.build_app_mention_request())
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1

    def test_installation_store_bot_only_oauth_settings(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            oauth_settings=self.oauth_settings_bot_only,
        )

        app.event("app_mention")(self.handle_app_mention)
        response = app.dispatch(self.build_app_mention_request())
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1

    def test_installation_store_bot_only_oauth_settings_conflicts(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            installation_store_bot_only=True,
            oauth_settings=self.oauth_settings,
        )

        app.event("app_mention")(self.handle_app_mention)
        response = app.dispatch(self.build_app_mention_request())
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1

    def test_installation_store_bot_only_oauth_flow(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            oauth_flow=OAuthFlow(settings=self.oauth_settings_bot_only),
        )

        app.event("app_mention")(self.handle_app_mention)
        response = app.dispatch(self.build_app_mention_request())
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1

    def test_installation_store_bot_only_oauth_flow_conflicts(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            installation_store_bot_only=True,
            oauth_flow=OAuthFlow(settings=self.oauth_settings),
        )

        app.event("app_mention")(self.handle_app_mention)
        response = app.dispatch(self.build_app_mention_request())
        assert response.status == 200
        assert_auth_test_count(self, 1)
        sleep(1)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1
Beispiel #27
0
class TestAWSLambda:
    signing_secret = "secret"
    valid_token = "xoxb-valid"
    mock_api_server_base_url = "http://localhost:8888"
    signature_verifier = SignatureVerifier(signing_secret)
    web_client = WebClient(
        token=valid_token,
        base_url=mock_api_server_base_url,
    )

    context = LambdaContext(function_name="test-function")

    def setup_method(self):
        self.old_os_env = remove_os_env_temporarily()
        setup_mock_web_api_server(self)

    def teardown_method(self):
        cleanup_mock_web_api_server(self)
        restore_os_env(self.old_os_env)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body,
            timestamp=timestamp,
        )

    def build_headers(self, timestamp: str, body: str):
        content_type = ("application/json" if body.startswith("{") else
                        "application/x-www-form-urlencoded")
        return {
            "content-type": [content_type],
            "x-slack-signature": [self.generate_signature(body, timestamp)],
            "x-slack-request-timestamp": [timestamp],
        }

    def test_not_found(self):
        response = not_found()
        assert response["statusCode"] == 404

    def test_first_value(self):
        assert _first_value({"foo": [1, 2, 3]}, "foo") == 1
        assert _first_value({"foo": []}, "foo") is None
        assert _first_value({}, "foo") is None

    @mock_lambda
    def test_clear_all_log_handlers(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )
        handler = SlackRequestHandler(app)
        handler.clear_all_log_handlers()

    @mock_lambda
    def test_events(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )

        def event_handler():
            pass

        app.event("app_mention")(event_handler)

        input = {
            "token": "verification_token",
            "team_id": "T111",
            "enterprise_id": "E111",
            "api_app_id": "A111",
            "event": {
                "client_msg_id": "9cbd4c5b-7ddf-4ede-b479-ad21fca66d63",
                "type": "app_mention",
                "text": "<@W111> Hi there!",
                "user": "******",
                "ts": "1595926230.009600",
                "team": "T111",
                "channel": "C111",
                "event_ts": "1595926230.009600",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1595926230,
            "authed_users": ["W111"],
        }
        timestamp, body = str(int(time())), json.dumps(input)
        event = {
            "body": body,
            "queryStringParameters": {},
            "headers": self.build_headers(timestamp, body),
            "requestContext": {
                "http": {
                    "method": "POST"
                }
            },
            "isBase64Encoded": False,
        }
        response = SlackRequestHandler(app).handle(event, self.context)
        assert response["statusCode"] == 200
        assert self.mock_received_requests["/auth.test"] == 1

        event = {
            "body": body,
            "queryStringParameters": {},
            "headers": self.build_headers(timestamp, body),
            "requestContext": {
                "httpMethod": "POST"
            },
            "isBase64Encoded": False,
        }
        response = SlackRequestHandler(app).handle(event, self.context)
        assert response["statusCode"] == 200
        assert self.mock_received_requests["/auth.test"] == 1

    @mock_lambda
    def test_shortcuts(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )

        def shortcut_handler(ack):
            ack()

        app.shortcut("test-shortcut")(shortcut_handler)

        input = {
            "type": "shortcut",
            "token": "verification_token",
            "action_ts": "111.111",
            "team": {
                "id": "T111",
                "domain": "workspace-domain",
                "enterprise_id": "E111",
                "enterprise_name": "Org Name",
            },
            "user": {
                "id": "W111",
                "username": "******",
                "team_id": "T111"
            },
            "callback_id": "test-shortcut",
            "trigger_id": "111.111.xxxxxx",
        }

        timestamp, body = str(int(
            time())), f"payload={quote(json.dumps(input))}"
        event = {
            "body": body,
            "queryStringParameters": {},
            "headers": self.build_headers(timestamp, body),
            "requestContext": {
                "http": {
                    "method": "POST"
                }
            },
            "isBase64Encoded": False,
        }
        response = SlackRequestHandler(app).handle(event, self.context)
        assert response["statusCode"] == 200
        assert self.mock_received_requests["/auth.test"] == 1

        event = {
            "body": body,
            "queryStringParameters": {},
            "headers": self.build_headers(timestamp, body),
            "requestContext": {
                "httpMethod": "POST"
            },
            "isBase64Encoded": False,
        }
        response = SlackRequestHandler(app).handle(event, self.context)
        assert response["statusCode"] == 200
        assert self.mock_received_requests["/auth.test"] == 1

    @mock_lambda
    def test_commands(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )

        def command_handler(ack):
            ack()

        app.command("/hello-world")(command_handler)

        input = (
            "token=verification_token"
            "&team_id=T111"
            "&team_domain=test-domain"
            "&channel_id=C111"
            "&channel_name=random"
            "&user_id=W111"
            "&user_name=primary-owner"
            "&command=%2Fhello-world"
            "&text=Hi"
            "&enterprise_id=E111"
            "&enterprise_name=Org+Name"
            "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx"
            "&trigger_id=111.111.xxx")
        timestamp, body = str(int(time())), input
        event = {
            "body": body,
            "queryStringParameters": {},
            "headers": self.build_headers(timestamp, body),
            "requestContext": {
                "http": {
                    "method": "POST"
                }
            },
            "isBase64Encoded": False,
        }
        response = SlackRequestHandler(app).handle(event, self.context)
        assert response["statusCode"] == 200
        assert self.mock_received_requests["/auth.test"] == 1

        event = {
            "body": body,
            "queryStringParameters": {},
            "headers": self.build_headers(timestamp, body),
            "requestContext": {
                "httpMethod": "POST"
            },
            "isBase64Encoded": False,
        }
        response = SlackRequestHandler(app).handle(event, self.context)
        assert response["statusCode"] == 200
        assert self.mock_received_requests["/auth.test"] == 1

    @mock_lambda
    def test_lazy_listeners(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )

        def command_handler(ack):
            ack()

        def say_it(say):
            say("Done!")

        app.command("/hello-world")(ack=command_handler, lazy=[say_it])

        input = (
            "token=verification_token"
            "&team_id=T111"
            "&team_domain=test-domain"
            "&channel_id=C111"
            "&channel_name=random"
            "&user_id=W111"
            "&user_name=primary-owner"
            "&command=%2Fhello-world"
            "&text=Hi"
            "&enterprise_id=E111"
            "&enterprise_name=Org+Name"
            "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx"
            "&trigger_id=111.111.xxx")
        timestamp, body = str(int(time())), input
        headers = self.build_headers(timestamp, body)
        headers["x-slack-bolt-lazy-only"] = "1"
        headers["x-slack-bolt-lazy-function-name"] = "say_it"
        event = {
            "body": body,
            "queryStringParameters": {},
            "headers": headers,
            "requestContext": {
                "http": {
                    "method": "NONE"
                }
            },
            "isBase64Encoded": False,
        }
        response = SlackRequestHandler(app).handle(event, self.context)
        assert response["statusCode"] == 200
        assert self.mock_received_requests["/auth.test"] == 1
        assert self.mock_received_requests["/chat.postMessage"] == 1

    @mock_lambda
    def test_oauth(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            oauth_settings=OAuthSettings(
                client_id="111.111",
                client_secret="xxx",
                scopes=["chat:write", "commands"],
            ),
        )

        event = {
            "body": "",
            "queryStringParameters": {},
            "headers": {},
            "requestContext": {
                "http": {
                    "method": "GET"
                }
            },
            "isBase64Encoded": False,
        }
        response = SlackRequestHandler(app).handle(event, self.context)
        assert response["statusCode"] == 200
        assert response["headers"][
            "content-type"] == "text/html; charset=utf-8"
        assert response["headers"]["content-length"] == "565"
        assert response.get("body") is not None

        event = {
            "body": "",
            "queryStringParameters": {},
            "headers": {},
            "requestContext": {
                "httpMethod": "GET"
            },
            "isBase64Encoded": False,
        }
        response = SlackRequestHandler(app).handle(event, self.context)
        assert response["statusCode"] == 200
        assert response["headers"][
            "content-type"] == "text/html; charset=utf-8"
        assert response["headers"]["content-length"] == "565"
        assert "https://slack.com/oauth/v2/authorize?state=" in response.get(
            "body")
Beispiel #28
0
class TestAppUsingMethodsInClass:
    signing_secret = "secret"
    valid_token = "xoxb-valid"
    mock_api_server_base_url = "http://localhost:8888"
    signature_verifier = SignatureVerifier(signing_secret)
    web_client = AsyncWebClient(
        token=valid_token,
        base_url=mock_api_server_base_url,
    )

    @pytest.fixture
    def event_loop(self):
        old_os_env = remove_os_env_temporarily()
        try:
            setup_mock_web_api_server(self)
            loop = asyncio.get_event_loop()
            yield loop
            loop.close()
            cleanup_mock_web_api_server(self)
        finally:
            restore_os_env(old_os_env)

    def test_inspect_behaviors(self):
        async def f():
            pass

        assert inspect.ismethod(f) is False

        class A:
            async def b(self):
                pass

            @classmethod
            async def c(cls):
                pass

            @staticmethod
            async def d():
                pass

        a = A()
        assert inspect.ismethod(a.b) is True
        assert inspect.ismethod(A.c) is True
        assert inspect.ismethod(A.d) is False

    async def run_app_and_verify(self, app: AsyncApp):
        payload = {
            "type": "message_action",
            "token": "verification_token",
            "action_ts": "1583637157.207593",
            "team": {
                "id": "T111",
                "domain": "test-test",
                "enterprise_id": "E111",
                "enterprise_name": "Org Name",
            },
            "user": {"id": "W111", "name": "test-test"},
            "channel": {"id": "C111", "name": "dev"},
            "callback_id": "test-shortcut",
            "trigger_id": "111.222.xxx",
            "message_ts": "1583636382.000300",
            "message": {
                "client_msg_id": "zzzz-111-222-xxx-yyy",
                "type": "message",
                "text": "<@W222> test",
                "user": "******",
                "ts": "1583636382.000300",
                "team": "T111",
                "blocks": [
                    {
                        "type": "rich_text",
                        "block_id": "d7eJ",
                        "elements": [
                            {
                                "type": "rich_text_section",
                                "elements": [
                                    {"type": "user", "user_id": "U222"},
                                    {"type": "text", "text": " test"},
                                ],
                            }
                        ],
                    }
                ],
            },
            "response_url": "https://hooks.slack.com/app/T111/111/xxx",
        }

        timestamp, body = str(int(time())), f"payload={json.dumps(payload)}"
        request: AsyncBoltRequest = AsyncBoltRequest(
            body=body,
            headers={
                "content-type": ["application/x-www-form-urlencoded"],
                "x-slack-signature": [
                    self.signature_verifier.generate_signature(
                        body=body,
                        timestamp=timestamp,
                    )
                ],
                "x-slack-request-timestamp": [timestamp],
            },
        )
        response = await app.async_dispatch(request)
        assert response.status == 200
        await assert_auth_test_count_async(self, 1)
        await asyncio.sleep(0.5)  # wait a bit after auto ack()
        assert self.mock_received_requests["/chat.postMessage"] == 1

    @pytest.mark.asyncio
    async def test_class_methods(self):
        app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret)
        app.use(AwesomeClass.class_middleware)
        app.shortcut("test-shortcut")(AwesomeClass.class_method)
        await self.run_app_and_verify(app)

    @pytest.mark.asyncio
    async def test_class_methods_uncommon_name(self):
        app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret)
        app.use(AwesomeClass.class_middleware)
        app.shortcut("test-shortcut")(AwesomeClass.class_method2)
        await self.run_app_and_verify(app)

    @pytest.mark.asyncio
    async def test_instance_methods(self):
        app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret)
        awesome = AwesomeClass("Slackbot")
        app.use(awesome.instance_middleware)
        app.shortcut("test-shortcut")(awesome.instance_method)
        await self.run_app_and_verify(app)

    @pytest.mark.asyncio
    async def test_instance_methods_uncommon_name_1(self):
        app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret)
        awesome = AwesomeClass("Slackbot")
        app.use(awesome.instance_middleware)
        app.shortcut("test-shortcut")(awesome.instance_method2)
        await self.run_app_and_verify(app)

    @pytest.mark.asyncio
    async def test_instance_methods_uncommon_name_2(self):
        app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret)
        awesome = AwesomeClass("Slackbot")
        app.use(awesome.instance_middleware)
        app.shortcut("test-shortcut")(awesome.instance_method3)
        await self.run_app_and_verify(app)

    @pytest.mark.asyncio
    async def test_static_methods(self):
        app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret)
        app.use(AwesomeClass.static_middleware)
        app.shortcut("test-shortcut")(AwesomeClass.static_method)
        await self.run_app_and_verify(app)

    @pytest.mark.asyncio
    async def test_invalid_arg_in_func(self):
        app = AsyncApp(client=self.web_client, signing_secret=self.signing_secret)
        app.shortcut("test-shortcut")(top_level_function)
        await self.run_app_and_verify(app)
Beispiel #29
0
class TestDjango(TestCase):
    signing_secret = "secret"
    valid_token = "xoxb-valid"
    mock_api_server_base_url = "http://*****:*****@W111> Hi there!",
                "user": "******",
                "ts": "1595926230.009600",
                "team": "T111",
                "channel": "C111",
                "event_ts": "1595926230.009600",
            },
            "type": "event_callback",
            "event_id": "Ev111",
            "event_time": 1595926230,
            "authed_users": ["W111"],
        }
        timestamp, body = str(int(time())), json.dumps(input)

        request = self.rf.post("/slack/events",
                               data=body,
                               content_type="application/json")
        request.headers = self.build_headers(timestamp, body)

        response = SlackRequestHandler(app).handle(request)
        assert response.status_code == 200
        assert_auth_test_count(self, 1)

    def test_shortcuts(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )

        def shortcut_handler(ack):
            ack()

        app.shortcut("test-shortcut")(shortcut_handler)

        input = {
            "type": "shortcut",
            "token": "verification_token",
            "action_ts": "111.111",
            "team": {
                "id": "T111",
                "domain": "workspace-domain",
                "enterprise_id": "E111",
                "enterprise_name": "Org Name",
            },
            "user": {
                "id": "W111",
                "username": "******",
                "team_id": "T111"
            },
            "callback_id": "test-shortcut",
            "trigger_id": "111.111.xxxxxx",
        }

        timestamp, body = str(int(
            time())), f"payload={quote(json.dumps(input))}"

        request = self.rf.post(
            "/slack/events",
            data=body,
            content_type="application/x-www-form-urlencoded",
        )
        request.headers = self.build_headers(timestamp, body)

        response = SlackRequestHandler(app).handle(request)
        assert response.status_code == 200
        assert_auth_test_count(self, 1)

    def test_commands(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )

        def command_handler(ack):
            ack()

        app.command("/hello-world")(command_handler)

        input = (
            "token=verification_token"
            "&team_id=T111"
            "&team_domain=test-domain"
            "&channel_id=C111"
            "&channel_name=random"
            "&user_id=W111"
            "&user_name=primary-owner"
            "&command=%2Fhello-world"
            "&text=Hi"
            "&enterprise_id=E111"
            "&enterprise_name=Org+Name"
            "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx"
            "&trigger_id=111.111.xxx")
        timestamp, body = str(int(time())), input

        request = self.rf.post(
            "/slack/events",
            data=body,
            content_type="application/x-www-form-urlencoded",
        )
        request.headers = self.build_headers(timestamp, body)

        response = SlackRequestHandler(app).handle(request)
        assert response.status_code == 200
        assert_auth_test_count(self, 1)

    def test_commands_process_before_response(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            process_before_response=True,
        )

        def command_handler(ack):
            ack()

        app.command("/hello-world")(command_handler)

        input = (
            "token=verification_token"
            "&team_id=T111"
            "&team_domain=test-domain"
            "&channel_id=C111"
            "&channel_name=random"
            "&user_id=W111"
            "&user_name=primary-owner"
            "&command=%2Fhello-world"
            "&text=Hi"
            "&enterprise_id=E111"
            "&enterprise_name=Org+Name"
            "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx"
            "&trigger_id=111.111.xxx")
        timestamp, body = str(int(time())), input

        request = self.rf.post(
            "/slack/events",
            data=body,
            content_type="application/x-www-form-urlencoded",
        )
        request.headers = self.build_headers(timestamp, body)

        response = SlackRequestHandler(app).handle(request)
        assert response.status_code == 200
        assert_auth_test_count(self, 1)

    def test_commands_lazy(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )

        def command_handler(ack):
            ack()

        def lazy_handler():
            pass

        app.command("/hello-world")(ack=command_handler, lazy=[lazy_handler])

        input = (
            "token=verification_token"
            "&team_id=T111"
            "&team_domain=test-domain"
            "&channel_id=C111"
            "&channel_name=random"
            "&user_id=W111"
            "&user_name=primary-owner"
            "&command=%2Fhello-world"
            "&text=Hi"
            "&enterprise_id=E111"
            "&enterprise_name=Org+Name"
            "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx"
            "&trigger_id=111.111.xxx")
        timestamp, body = str(int(time())), input

        request = self.rf.post(
            "/slack/events",
            data=body,
            content_type="application/x-www-form-urlencoded",
        )
        request.headers = self.build_headers(timestamp, body)

        response = SlackRequestHandler(app).handle(request)
        assert response.status_code == 200
        assert_auth_test_count(self, 1)

    def test_oauth(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            oauth_settings=OAuthSettings(
                client_id="111.111",
                client_secret="xxx",
                scopes=["chat:write", "commands"],
            ),
        )
        request = self.rf.get("/slack/install")
        response = SlackRequestHandler(app).handle(request)
        assert response.status_code == 200
        assert response.get("content-type") == "text/html; charset=utf-8"
        assert "https://slack.com/oauth/v2/authorize?state=" in response.content.decode(
            "utf-8")
class TestSlashCommand:
    signing_secret = "secret"
    valid_token = "xoxb-valid"
    mock_api_server_base_url = "http://localhost:8888"
    signature_verifier = SignatureVerifier(signing_secret)
    web_client = WebClient(
        token=valid_token,
        base_url=mock_api_server_base_url,
    )

    def setup_method(self):
        self.old_os_env = remove_os_env_temporarily()
        setup_mock_web_api_server(self)

    def teardown_method(self):
        cleanup_mock_web_api_server(self)
        restore_os_env(self.old_os_env)

    def generate_signature(self, body: str, timestamp: str):
        return self.signature_verifier.generate_signature(
            body=body,
            timestamp=timestamp,
        )

    def build_headers(self, timestamp: str, body: str):
        return {
            "content-type": ["application/x-www-form-urlencoded"],
            "x-slack-signature": [self.generate_signature(body, timestamp)],
            "x-slack-request-timestamp": [timestamp],
        }

    def build_valid_request(self) -> BoltRequest:
        timestamp, body = str(int(time())), json.dumps(slash_command_payload)
        return BoltRequest(body=body,
                           headers=self.build_headers(timestamp, body))

    def test_mock_server_is_running(self):
        resp = self.web_client.api_test()
        assert resp != None

    def test_success(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )
        app.command("/hello-world")(commander)

        request = self.build_valid_request()
        response = app.dispatch(request)
        assert response.status == 200
        assert self.mock_received_requests["/auth.test"] == 1

    def test_process_before_response(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
            process_before_response=True,
        )
        app.command("/hello-world")(commander)

        request = self.build_valid_request()
        response = app.dispatch(request)
        assert response.status == 200
        assert self.mock_received_requests["/auth.test"] == 1

    def test_failure(self):
        app = App(
            client=self.web_client,
            signing_secret=self.signing_secret,
        )
        request = self.build_valid_request()
        response = app.dispatch(request)
        assert response.status == 404
        assert self.mock_received_requests["/auth.test"] == 1

        app.command("/another-one")(commander)
        response = app.dispatch(request)
        assert response.status == 404
        assert self.mock_received_requests["/auth.test"] == 2