WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit b7eb2f9

Browse files
fix: move away from datetime.utcfromtimestamp for the state and installation stores (#1798)
1 parent dfa5e5f commit b7eb2f9

File tree

6 files changed

+76
-15
lines changed

6 files changed

+76
-15
lines changed

integration_tests/samples/oauth/oauth_v2.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,19 @@ def oauth_callback():
123123

124124
@app.route("/slack/events", methods=["POST"])
125125
def slack_app():
126+
data = request.get_data()
126127
if not signature_verifier.is_valid(
127-
body=request.get_data(),
128+
body=data,
128129
timestamp=request.headers.get("X-Slack-Request-Timestamp"),
129130
signature=request.headers.get("X-Slack-Signature"),
130131
):
131132
return make_response("invalid request", 403)
132133

134+
if data and b"url_verification" in data:
135+
body = json.loads(data)
136+
if body.get("type") == "url_verification" and "challenge" in body:
137+
return make_response(body["challenge"], 200)
138+
133139
if "command" in request.form and request.form["command"] == "/open-modal":
134140
try:
135141
enterprise_id = request.form.get("enterprise_id")
@@ -193,4 +199,4 @@ def slack_app():
193199

194200
# python3 integration_tests/samples/oauth/oauth_v2.py
195201
# ngrok http 3000
196-
# https://{yours}.ngrok.io/slack/oauth/start
202+
# https://{yours}.ngrok.io/slack/install

integration_tests/samples/oauth/oauth_v2_async.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,19 @@ async def oauth_callback(req: Request):
135135

136136
@app.post("/slack/events")
137137
async def slack_app(req: Request):
138+
data = req.body.decode("utf-8")
138139
if not signature_verifier.is_valid(
139-
body=req.body.decode("utf-8"),
140+
body=data,
140141
timestamp=req.headers.get("X-Slack-Request-Timestamp"),
141142
signature=req.headers.get("X-Slack-Signature"),
142143
):
143144
return HTTPResponse(status=403, body="invalid request")
144145

146+
if data and "url_verification" in data:
147+
body = json.loads(data)
148+
if body.get("type") == "url_verification" and "challenge" in body:
149+
return HTTPResponse(status=200, body=body["challenge"])
150+
145151
if "command" in req.form and req.form.get("command") == "/open-modal":
146152
try:
147153
enterprise_id = req.form.get("enterprise_id")

slack_sdk/oauth/installation_store/models/bot.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import datetime
1+
from datetime import datetime, timezone
22
from time import time
33
from typing import Optional, Union, Dict, Any, Sequence
44

@@ -100,10 +100,12 @@ def _to_standard_value_dict(self) -> Dict[str, Any]:
100100
"bot_scopes": ",".join(self.bot_scopes) if self.bot_scopes else None,
101101
"bot_refresh_token": self.bot_refresh_token,
102102
"bot_token_expires_at": (
103-
datetime.utcfromtimestamp(self.bot_token_expires_at) if self.bot_token_expires_at is not None else None
103+
datetime.fromtimestamp(self.bot_token_expires_at, tz=timezone.utc)
104+
if self.bot_token_expires_at is not None
105+
else None
104106
),
105107
"is_enterprise_install": self.is_enterprise_install,
106-
"installed_at": datetime.utcfromtimestamp(self.installed_at),
108+
"installed_at": datetime.fromtimestamp(self.installed_at, tz=timezone.utc),
107109
}
108110

109111
def to_dict_for_copying(self) -> Dict[str, Any]:

slack_sdk/oauth/installation_store/models/installation.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import datetime
1+
from datetime import datetime, timezone
22
from time import time
33
from typing import Optional, Union, Dict, Any, Sequence
44

@@ -173,22 +173,26 @@ def _to_standard_value_dict(self) -> Dict[str, Any]:
173173
"bot_scopes": ",".join(self.bot_scopes) if self.bot_scopes else None,
174174
"bot_refresh_token": self.bot_refresh_token,
175175
"bot_token_expires_at": (
176-
datetime.utcfromtimestamp(self.bot_token_expires_at) if self.bot_token_expires_at is not None else None
176+
datetime.fromtimestamp(self.bot_token_expires_at, tz=timezone.utc)
177+
if self.bot_token_expires_at is not None
178+
else None
177179
),
178180
"user_id": self.user_id,
179181
"user_token": self.user_token,
180182
"user_scopes": ",".join(self.user_scopes) if self.user_scopes else None,
181183
"user_refresh_token": self.user_refresh_token,
182184
"user_token_expires_at": (
183-
datetime.utcfromtimestamp(self.user_token_expires_at) if self.user_token_expires_at is not None else None
185+
datetime.fromtimestamp(self.user_token_expires_at, tz=timezone.utc)
186+
if self.user_token_expires_at is not None
187+
else None
184188
),
185189
"incoming_webhook_url": self.incoming_webhook_url,
186190
"incoming_webhook_channel": self.incoming_webhook_channel,
187191
"incoming_webhook_channel_id": self.incoming_webhook_channel_id,
188192
"incoming_webhook_configuration_url": self.incoming_webhook_configuration_url,
189193
"is_enterprise_install": self.is_enterprise_install,
190194
"token_type": self.token_type,
191-
"installed_at": datetime.utcfromtimestamp(self.installed_at),
195+
"installed_at": datetime.fromtimestamp(self.installed_at, tz=timezone.utc),
192196
}
193197

194198
def to_dict_for_copying(self) -> Dict[str, Any]:

slack_sdk/oauth/state_store/sqlalchemy/__init__.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22
import time
3-
from datetime import datetime
3+
from datetime import datetime, timezone
44
from logging import Logger
55
from uuid import uuid4
66

@@ -55,7 +55,7 @@ def logger(self) -> Logger:
5555

5656
def issue(self, *args, **kwargs) -> str:
5757
state: str = str(uuid4())
58-
now = datetime.utcfromtimestamp(time.time() + self.expiration_seconds)
58+
now = datetime.fromtimestamp(time.time() + self.expiration_seconds, tz=timezone.utc)
5959
with self.engine.begin() as conn:
6060
conn.execute(
6161
self.oauth_states.insert(),
@@ -67,7 +67,7 @@ def consume(self, state: str) -> bool:
6767
try:
6868
with self.engine.begin() as conn:
6969
c = self.oauth_states.c
70-
query = self.oauth_states.select().where(and_(c.state == state, c.expire_at > datetime.utcnow()))
70+
query = self.oauth_states.select().where(and_(c.state == state, c.expire_at > datetime.now(tz=timezone.utc)))
7171
result = conn.execute(query)
7272
for row in result.mappings():
7373
self.logger.debug(f"consume's query result: {row}")
@@ -124,7 +124,7 @@ def logger(self) -> Logger:
124124

125125
async def async_issue(self, *args, **kwargs) -> str:
126126
state: str = str(uuid4())
127-
now = datetime.utcfromtimestamp(time.time() + self.expiration_seconds)
127+
now = datetime.fromtimestamp(time.time() + self.expiration_seconds, tz=timezone.utc)
128128
async with self.engine.begin() as conn:
129129
await conn.execute(
130130
self.oauth_states.insert(),
@@ -136,7 +136,7 @@ async def async_consume(self, state: str) -> bool:
136136
try:
137137
async with self.engine.begin() as conn:
138138
c = self.oauth_states.c
139-
query = self.oauth_states.select().where(and_(c.state == state, c.expire_at > datetime.utcnow()))
139+
query = self.oauth_states.select().where(and_(c.state == state, c.expire_at > datetime.now(tz=timezone.utc)))
140140
result = await conn.execute(query)
141141
for row in result.mappings():
142142
self.logger.debug(f"consume's query result: {row}")

tests/slack_sdk/oauth/installation_store/test_models.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import time
2+
from datetime import datetime, timezone
23
import unittest
34

45
from slack_sdk.oauth.installation_store import Installation, FileInstallationStore, Bot
@@ -36,6 +37,22 @@ def test_bot_custom_fields(self):
3637
self.assertEqual(bot.to_dict().get("service_user_id"), "XYZ123")
3738
self.assertEqual(bot.to_dict_for_copying().get("custom_values").get("service_user_id"), "XYZ123")
3839

40+
def test_bot_datetime_manipulation(self):
41+
expected_timestamp = datetime.now(tz=timezone.utc)
42+
bot = Bot(
43+
bot_token="xoxb-",
44+
bot_id="B111",
45+
bot_user_id="U111",
46+
bot_token_expires_at=expected_timestamp,
47+
installed_at=expected_timestamp,
48+
)
49+
bot_dict = bot.to_dict()
50+
self.assertIsNotNone(bot_dict)
51+
self.assertEqual(
52+
bot_dict.get("bot_token_expires_at").isoformat(), expected_timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00")
53+
)
54+
self.assertEqual(bot_dict.get("installed_at"), expected_timestamp)
55+
3956
def test_installation(self):
4057
installation = Installation(
4158
app_id="A111",
@@ -84,3 +101,29 @@ def test_installation_custom_fields(self):
84101
self.assertEqual(bot.to_dict().get("app_id"), "A111")
85102
self.assertEqual(bot.to_dict().get("service_user_id"), "XYZ123")
86103
self.assertEqual(bot.to_dict_for_copying().get("custom_values").get("app_id"), "A222")
104+
105+
def test_installation_datetime_manipulation(self):
106+
expected_timestamp = datetime.now(tz=timezone.utc)
107+
installation = Installation(
108+
app_id="A111",
109+
enterprise_id="E111",
110+
team_id="T111",
111+
user_id="U111",
112+
bot_id="B111",
113+
bot_token="xoxb-111",
114+
bot_scopes=["chat:write"],
115+
bot_user_id="U222",
116+
bot_token_expires_at=expected_timestamp,
117+
user_token_expires_at=expected_timestamp,
118+
installed_at=expected_timestamp,
119+
)
120+
installation_dict = installation.to_dict()
121+
self.assertIsNotNone(installation_dict)
122+
self.assertEqual(
123+
installation_dict.get("bot_token_expires_at").isoformat(), expected_timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00")
124+
)
125+
self.assertEqual(
126+
installation_dict.get("user_token_expires_at").isoformat(),
127+
expected_timestamp.strftime("%Y-%m-%dT%H:%M:%S+00:00"),
128+
)
129+
self.assertEqual(installation_dict.get("installed_at"), expected_timestamp)

0 commit comments

Comments
 (0)