Use sns credentials from VCAP_SERVICES

This commit is contained in:
Ryan Ahearn
2023-02-28 16:50:00 -05:00
parent 439722990e
commit 28f8649444
8 changed files with 40 additions and 28 deletions

View File

@@ -14,7 +14,6 @@ env:
WERKZEUG_DEBUG_PIN: off WERKZEUG_DEBUG_PIN: off
REDIS_ENABLED: 0 REDIS_ENABLED: 0
AWS_REGION: us-west-2 AWS_REGION: us-west-2
AWS_PINPOINT_REGION: us-west-2
AWS_US_TOLL_FREE_NUMBER: +18446120782 AWS_US_TOLL_FREE_NUMBER: +18446120782
jobs: jobs:

View File

@@ -19,7 +19,6 @@ env:
NOTIFY_EMAIL_DOMAIN: dispostable.com NOTIFY_EMAIL_DOMAIN: dispostable.com
REDIS_ENABLED: 0 REDIS_ENABLED: 0
AWS_REGION: us-west-2 AWS_REGION: us-west-2
AWS_PINPOINT_REGION: us-west-2
AWS_US_TOLL_FREE_NUMBER: +18446120782 AWS_US_TOLL_FREE_NUMBER: +18446120782
jobs: jobs:

View File

@@ -1,11 +1,11 @@
import re
from time import monotonic from time import monotonic
import boto3
import botocore import botocore
import phonenumbers import phonenumbers
from boto3 import client
from app.clients.sms import SmsClient from app.clients.sms import SmsClient
from app.cloudfoundry_config import cloud_config
class AwsSnsClient(SmsClient): class AwsSnsClient(SmsClient):
@@ -14,19 +14,22 @@ class AwsSnsClient(SmsClient):
""" """
def init_app(self, current_app, statsd_client, *args, **kwargs): def init_app(self, current_app, statsd_client, *args, **kwargs):
self._client = boto3.client("sns", region_name=current_app.config["AWS_REGION"]) self._client = client(
self._long_codes_client = boto3.client("sns", region_name=current_app.config["AWS_PINPOINT_REGION"]) "sns",
region_name=cloud_config.sns_region,
aws_access_key_id=cloud_config.sns_access_key,
aws_secret_access_key=cloud_config.sns_secret_key
)
super(SmsClient, self).__init__(*args, **kwargs) super(SmsClient, self).__init__(*args, **kwargs)
self.current_app = current_app self.current_app = current_app
self.statsd_client = statsd_client self.statsd_client = statsd_client
self.long_code_regex = re.compile(r"^\+1\d{10}$")
@property @property
def name(self): def name(self):
return 'sns' return 'sns'
def get_name(self): def get_name(self):
return 'sns' return self.name
def send_sms(self, to, content, reference, sender=None, international=False): def send_sms(self, to, content, reference, sender=None, international=False):
matched = False matched = False
@@ -35,7 +38,6 @@ class AwsSnsClient(SmsClient):
matched = True matched = True
to = phonenumbers.format_number(match.number, phonenumbers.PhoneNumberFormat.E164) to = phonenumbers.format_number(match.number, phonenumbers.PhoneNumberFormat.E164)
client = self._client
# See documentation # See documentation
# https://docs.aws.amazon.com/sns/latest/dg/sms_publish-to-phone.html#sms_publish_sdk # https://docs.aws.amazon.com/sns/latest/dg/sms_publish-to-phone.html#sms_publish_sdk
attributes = { attributes = {
@@ -45,20 +47,12 @@ class AwsSnsClient(SmsClient):
} }
} }
# If sending with a long code number, we need to use another AWS region if sender:
# and specify the phone number we want to use as the origination number
send_with_dedicated_phone_number = self._send_with_dedicated_phone_number(sender)
if send_with_dedicated_phone_number:
client = self._long_codes_client
attributes["AWS.MM.SMS.OriginationNumber"] = { attributes["AWS.MM.SMS.OriginationNumber"] = {
"DataType": "String", "DataType": "String",
"StringValue": sender, "StringValue": sender,
} }
else:
# If the number is US based, we must use a US Toll Free number to send the message
country = phonenumbers.region_code_for_number(match.number)
if country == "US":
client = self._long_codes_client
attributes["AWS.MM.SMS.OriginationNumber"] = { attributes["AWS.MM.SMS.OriginationNumber"] = {
"DataType": "String", "DataType": "String",
"StringValue": self.current_app.config["AWS_US_TOLL_FREE_NUMBER"], "StringValue": self.current_app.config["AWS_US_TOLL_FREE_NUMBER"],
@@ -66,7 +60,7 @@ class AwsSnsClient(SmsClient):
try: try:
start_time = monotonic() start_time = monotonic()
response = client.publish(PhoneNumber=to, Message=content, MessageAttributes=attributes) response = self._client.publish(PhoneNumber=to, Message=content, MessageAttributes=attributes)
except botocore.exceptions.ClientError as e: except botocore.exceptions.ClientError as e:
self.statsd_client.incr("clients.sns.error") self.statsd_client.incr("clients.sns.error")
raise str(e) raise str(e)
@@ -84,6 +78,3 @@ class AwsSnsClient(SmsClient):
self.statsd_client.incr("clients.sns.error") self.statsd_client.incr("clients.sns.error")
self.current_app.logger.error("No valid numbers found in {}".format(to)) self.current_app.logger.error("No valid numbers found in {}".format(to))
raise ValueError("No valid numbers found for SMS delivery") raise ValueError("No valid numbers found for SMS delivery")
def _send_with_dedicated_phone_number(self, sender):
return sender and re.match(self.long_code_regex, sender)

View File

@@ -59,6 +59,27 @@ class CloudfoundryConfig:
except KeyError: except KeyError:
return getenv('AWS_SECRET_ACCESS_KEY') return getenv('AWS_SECRET_ACCESS_KEY')
@property
def sns_access_key(self):
try:
return self._sns_credentials('aws_access_key_id')
except KeyError:
return getenv('AWS_ACCESS_KEY_ID')
@property
def sns_secret_key(self):
try:
return self._sns_credentials('aws_secret_access_key')
except KeyError:
return getenv('AWS_SECRET_ACCESS_KEY')
@property
def sns_region(self):
try:
return self._sns_credentials('region')
except KeyError:
return getenv('AWS_REGION')
@property @property
def sns_topic_arns(self): def sns_topic_arns(self):
try: try:
@@ -73,5 +94,8 @@ class CloudfoundryConfig:
def _ses_credentials(self, key): def _ses_credentials(self, key):
return self.parsed_services['datagov-smtp'][0]['credentials'][key] return self.parsed_services['datagov-smtp'][0]['credentials'][key]
def _sns_credentials(self, key):
return self.parsed_services['ttsnotify-sms'][0]['credentials'][key]
cloud_config = CloudfoundryConfig() cloud_config = CloudfoundryConfig()

View File

@@ -104,7 +104,6 @@ class Config(object):
# AWS Settings # AWS Settings
AWS_REGION = getenv('AWS_REGION') AWS_REGION = getenv('AWS_REGION')
AWS_PINPOINT_REGION = getenv("AWS_PINPOINT_REGION")
AWS_US_TOLL_FREE_NUMBER = getenv("AWS_US_TOLL_FREE_NUMBER") AWS_US_TOLL_FREE_NUMBER = getenv("AWS_US_TOLL_FREE_NUMBER")
# Whether to ignore POSTs from SNS for replies to SMS we sent # Whether to ignore POSTs from SNS for replies to SMS we sent
RECEIVE_INBOUND_SMS = False RECEIVE_INBOUND_SMS = False

View File

@@ -50,8 +50,6 @@ applications:
SECRET_KEY: ((SECRET_KEY)) SECRET_KEY: ((SECRET_KEY))
AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID)) AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID))
AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY)) AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY))
AWS_REGION: us-west-2
AWS_PINPOINT_REGION: us-west-2
AWS_US_TOLL_FREE_NUMBER: ((default_toll_free_number)) AWS_US_TOLL_FREE_NUMBER: ((default_toll_free_number))
REQUESTS_CA_BUNDLE: "/etc/ssl/certs/ca-certificates.crt" REQUESTS_CA_BUNDLE: "/etc/ssl/certs/ca-certificates.crt"

View File

@@ -19,7 +19,6 @@
AWS_ACCESS_KEY_ID="don't write secrets to the sample file" AWS_ACCESS_KEY_ID="don't write secrets to the sample file"
AWS_SECRET_ACCESS_KEY="don't write secrets to the sample file" AWS_SECRET_ACCESS_KEY="don't write secrets to the sample file"
AWS_REGION=us-west-2 AWS_REGION=us-west-2
AWS_PINPOINT_REGION=us-west-2
AWS_US_TOLL_FREE_NUMBER=+18446120782 AWS_US_TOLL_FREE_NUMBER=+18446120782
############################################################# #############################################################

View File

@@ -13,7 +13,10 @@ def test_send_sms_successful_returns_aws_sns_response(notify_api, mocker):
boto_mock.publish.assert_called_once_with( boto_mock.publish.assert_called_once_with(
PhoneNumber="+16135555555", PhoneNumber="+16135555555",
Message=content, Message=content,
MessageAttributes={'AWS.SNS.SMS.SMSType': {'DataType': 'String', 'StringValue': 'Transactional'}} MessageAttributes={
'AWS.SNS.SMS.SMSType': {'DataType': 'String', 'StringValue': 'Transactional'},
'AWS.MM.SMS.OriginationNumber': {'DataType': 'String', 'StringValue': '+18446120782'}
}
) )