Skip to content

Commit d0cdd84

Browse files
authored
🐛 Is91/fix alphanumeric sender (⚠️ devops) (ITISFoundation#3323)
1 parent 2cc2946 commit d0cdd84

File tree

4 files changed

+77
-14
lines changed

4 files changed

+77
-14
lines changed

packages/settings-library/setup.cfg

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ universal = 1
1414
# Define setup.py command aliases here
1515
test = pytest
1616

17-
[tool:pytest]
18-
asyncio_mode = auto
17+
# NOTE: uncomment when pytest-asyncio is added in requirements
18+
# [tool:pytest]
19+
# asyncio_mode = auto

packages/settings-library/src/settings_library/twilio.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,33 @@
66
"""
77

88

9-
from pydantic import Field
9+
from pydantic import Field, constr
1010

1111
from .base import BaseCustomSettings
1212

13+
# Based on https://countrycode.org/
14+
CountryCodeStr = constr(strip_whitespace=True, regex=r"^\d{1,4}")
15+
1316

1417
class TwilioSettings(BaseCustomSettings):
1518
TWILIO_ACCOUNT_SID: str = Field(..., description="Twilio account String Identifier")
1619
TWILIO_AUTH_TOKEN: str = Field(..., description="API tokens")
20+
TWILIO_COUNTRY_CODES_W_ALPHANUMERIC_SID_SUPPORT: list[CountryCodeStr] = Field(
21+
[
22+
"41",
23+
],
24+
description="list of country-codes supporting/registered for alphanumeric sender ID"
25+
"See https://support.twilio.com/hc/en-us/articles/223133767-International-support-for-Alphanumeric-Sender-ID",
26+
)
27+
28+
def is_alphanumeric_supported(self, phone_number: str) -> bool:
29+
# Some countries do not support alphanumeric serder ID
30+
#
31+
# SEE https://support.twilio.com/hc/en-us/articles/223181348-Alphanumeric-Sender-ID-for-Twilio-Programmable-SMS
32+
phone_number_wo_international_code = (
33+
phone_number.strip().removeprefix("00").lstrip("+")
34+
)
35+
return any(
36+
phone_number_wo_international_code.startswith(code)
37+
for code in self.TWILIO_COUNTRY_CODES_W_ALPHANUMERIC_SID_SUPPORT
38+
)

packages/settings-library/tests/test_twilio.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,43 @@ def test_twilio_settings_within_envdevel(
2323
{
2424
"TWILIO_ACCOUNT_SID": "fake-account",
2525
"TWILIO_AUTH_TOKEN": "fake-token",
26-
"TWILIO_MESSAGING_SID": "x" * 34,
2726
},
2827
)
29-
TwilioSettings.create_from_envs()
28+
settings = TwilioSettings.create_from_envs()
29+
print(settings.json(indent=2))
30+
assert settings
31+
32+
33+
def test_twilio_settings_with_country_codes(
34+
mock_env_devel_environment: dict[str, str], monkeypatch: MonkeyPatch
35+
):
36+
37+
# defaults
38+
with monkeypatch.context() as patch:
39+
setenvs_from_dict(
40+
patch,
41+
{
42+
"TWILIO_ACCOUNT_SID": "fake-account",
43+
"TWILIO_AUTH_TOKEN": "fake-token",
44+
},
45+
)
46+
settings = TwilioSettings.create_from_envs()
47+
48+
assert settings.is_alphanumeric_supported("+41 456 789 156")
49+
assert not settings.is_alphanumeric_supported(" +1 123456 789 456 ")
50+
51+
# custom country codes
52+
with monkeypatch.context() as patch:
53+
setenvs_from_dict(
54+
patch,
55+
{
56+
"TWILIO_ACCOUNT_SID": "fake-account",
57+
"TWILIO_AUTH_TOKEN": "fake-token",
58+
"TWILIO_COUNTRY_CODES_W_ALPHANUMERIC_SID_SUPPORT": "[1, 34]",
59+
},
60+
)
61+
settings = TwilioSettings.create_from_envs()
62+
63+
assert not settings.is_alphanumeric_supported("+41 456 789 156")
64+
assert settings.is_alphanumeric_supported(" 001 123456 789 456 ")
65+
assert settings.is_alphanumeric_supported("+1 123456 789 456 ")

services/web/server/src/simcore_service_webserver/login/_2fa.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
from aiohttp import web
1616
from pydantic import BaseModel, Field
1717
from servicelib.logging_utils import log_decorator
18+
from settings_library.twilio import TwilioSettings
1819
from twilio.rest import Client
1920

2021
from ..redis import get_redis_validation_code_client
21-
from .settings import TwilioSettings
2222

2323
log = logging.getLogger(__name__)
2424

@@ -82,28 +82,32 @@ async def send_sms_code(
8282
twilio_alpha_numeric_sender: str,
8383
user_name: str = "user",
8484
):
85-
def sender():
85+
create_kwargs = {
86+
"messaging_service_sid": twilio_messaging_sid,
87+
"to": phone_number,
88+
"body": f"Dear {user_name[:20].capitalize().strip()}, your verification code is {code}",
89+
}
90+
if twilo_auth.is_alpha_numeric_supported(phone_number):
91+
create_kwargs["from_"] = twilio_alpha_numeric_sender
92+
93+
def _sender():
8694
log.info(
8795
"Sending sms code to %s from product %s",
8896
f"{phone_number=}",
8997
twilio_alpha_numeric_sender,
9098
)
99+
#
91100
# SEE https://www.twilio.com/docs/sms/quickstart/python
92101
#
93102
client = Client(twilo_auth.TWILIO_ACCOUNT_SID, twilo_auth.TWILIO_AUTH_TOKEN)
94-
message = client.messages.create(
95-
messaging_service_sid=twilio_messaging_sid,
96-
from_=twilio_alpha_numeric_sender,
97-
to=phone_number,
98-
body=f"Dear {user_name[:20].capitalize().strip()}, your verification code is {code}",
99-
)
103+
message = client.messages.create(**create_kwargs)
100104

101105
log.debug(
102106
"Got twilio client %s",
103107
f"{message=}",
104108
)
105109

106-
await asyncio.get_event_loop().run_in_executor(None, sender)
110+
await asyncio.get_event_loop().run_in_executor(None, _sender)
107111

108112

109113
#

0 commit comments

Comments
 (0)