mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-02 17:31:14 -05:00
test branch for notify-api-alt temporary deploy
This commit is contained in:
2
Makefile
2
Makefile
@@ -71,7 +71,7 @@ test: ## Run tests
|
|||||||
freeze-requirements: ## Pin all requirements including sub dependencies into requirements.txt
|
freeze-requirements: ## Pin all requirements including sub dependencies into requirements.txt
|
||||||
pip install --upgrade pip-tools
|
pip install --upgrade pip-tools
|
||||||
pip-compile requirements.in
|
pip-compile requirements.in
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt --no-cache-dir
|
||||||
|
|
||||||
.PHONY: audit
|
.PHONY: audit
|
||||||
audit:
|
audit:
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ from json import decoder
|
|||||||
|
|
||||||
import iso8601
|
import iso8601
|
||||||
import requests
|
import requests
|
||||||
|
import traceback
|
||||||
from celery.exceptions import Retry
|
from celery.exceptions import Retry
|
||||||
from flask import Blueprint, current_app, json, jsonify, request
|
from flask import Blueprint, current_app, json, jsonify, request
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
|
||||||
from app import notify_celery, statsd_client
|
from app import notify_celery, statsd_client, redis_store
|
||||||
from app.celery.validate_sns import valid_sns_message
|
# from app.celery.validate_sns import valid_sns_message
|
||||||
|
import validatesns
|
||||||
from app.config import QueueNames
|
from app.config import QueueNames
|
||||||
from app.dao import notifications_dao
|
from app.dao import notifications_dao
|
||||||
from app.errors import InvalidRequest, register_errors
|
from app.errors import InvalidRequest, register_errors
|
||||||
@@ -23,6 +25,7 @@ from app.notifications.notifications_ses_callback import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
ses_callback_blueprint = Blueprint('notifications_ses_callback', __name__)
|
ses_callback_blueprint = Blueprint('notifications_ses_callback', __name__)
|
||||||
|
DEFAULT_MAX_AGE = timedelta(days=10000)
|
||||||
|
|
||||||
register_errors(ses_callback_blueprint)
|
register_errors(ses_callback_blueprint)
|
||||||
class SNSMessageType(enum.Enum):
|
class SNSMessageType(enum.Enum):
|
||||||
@@ -41,6 +44,13 @@ def verify_message_type(message_type: str):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
raise InvalidMessageTypeException(f'{message_type} is not a valid message type.')
|
raise InvalidMessageTypeException(f'{message_type} is not a valid message type.')
|
||||||
|
|
||||||
|
def get_certificate(url):
|
||||||
|
res = redis_store.get(url)
|
||||||
|
if res is not None:
|
||||||
|
return res
|
||||||
|
res = requests.get(url).content
|
||||||
|
redis_store.set(url, res, ex=60 * 60) # 60 minutes
|
||||||
|
return res
|
||||||
|
|
||||||
# 400 counts as a permanent failure so SNS will not retry.
|
# 400 counts as a permanent failure so SNS will not retry.
|
||||||
# 500 counts as a failed delivery attempt so SNS will retry.
|
# 500 counts as a failed delivery attempt so SNS will retry.
|
||||||
@@ -64,15 +74,26 @@ def sns_callback_handler():
|
|||||||
raise InvalidRequest("SES-SNS callback failed: invalid JSON given", 400)
|
raise InvalidRequest("SES-SNS callback failed: invalid JSON given", 400)
|
||||||
|
|
||||||
current_app.logger.info(f"Message type: {message_type}\nResponse data: {message}")
|
current_app.logger.info(f"Message type: {message_type}\nResponse data: {message}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if valid_sns_message(message) == False:
|
# AWS sends SigningCertURL if sending to a webhook, but SigningCertUrl if sending to a Lambda function
|
||||||
current_app.logger.error(f"SES-SNS callback failed: validation failed! Response headers: {request.headers}\nResponse data: {request.data}\nError: Signature validation failed.")
|
message["SigningCertURL"] = message["SigningCertURL"] if "SigningCertURL" in message else message["SigningCertUrl"]
|
||||||
raise InvalidRequest("SES-SNS callback failed: validation failed", 400)
|
# Some SNS messages now contain "Subject": null, which is not handled by the validatesns library
|
||||||
except Exception as e:
|
if "Subject" in message and message["Subject"] == None:
|
||||||
current_app.logger.exception(f"SES-SNS callback failed: validation failed! Response headers: {request.headers}\nResponse data: {request.data}\nError: {e}")
|
message.pop("Subject")
|
||||||
|
validatesns.validate(message, get_certificate=get_certificate, max_age=DEFAULT_MAX_AGE)
|
||||||
|
except Exception as err:
|
||||||
|
current_app.logger.error(f"SES-SNS callback failed: validation failed! Response headers: {request.headers}\nResponse data: {request.data}\nError: Signature validation failed with error {err} and traceback {traceback.format_exc()}")
|
||||||
raise InvalidRequest("SES-SNS callback failed: validation failed", 400)
|
raise InvalidRequest("SES-SNS callback failed: validation failed", 400)
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# if valid_sns_message(message) == False:
|
||||||
|
# current_app.logger.error(f"SES-SNS callback failed: validation failed! Response headers: {request.headers}\nResponse data: {request.data}\nError: Signature validation failed.")
|
||||||
|
# raise InvalidRequest("SES-SNS callback failed: validation failed", 400)
|
||||||
|
# except Exception as e:
|
||||||
|
# current_app.logger.exception(f"SES-SNS callback failed: validation failed! Response headers: {request.headers}\nResponse data: {request.data}\nError: {e}")
|
||||||
|
# raise InvalidRequest("SES-SNS callback failed: validation failed", 400)
|
||||||
|
|
||||||
if message.get('Type') == 'SubscriptionConfirmation':
|
if message.get('Type') == 'SubscriptionConfirmation':
|
||||||
url = message.get('SubscribeUrl') if 'SubscribeUrl' in message else message.get('SubscribeURL')
|
url = message.get('SubscribeUrl') if 'SubscribeUrl' in message else message.get('SubscribeURL')
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ def extract_cloudfoundry_config():
|
|||||||
vcap_services = json.loads(os.environ['VCAP_SERVICES'])
|
vcap_services = json.loads(os.environ['VCAP_SERVICES'])
|
||||||
|
|
||||||
# Postgres config
|
# Postgres config
|
||||||
os.environ['SQLALCHEMY_DATABASE_URI'] = vcap_services['aws-rds'][0]['credentials']['uri'].replace('postgres',
|
os.environ['SQLALCHEMY_DATABASE_URI'] = vcap_services['aws-rds'][0]['credentials']['uri'].replace('postgres','postgresql')
|
||||||
'postgresql')
|
|
||||||
# Redis config
|
# Redis config
|
||||||
os.environ['REDIS_URL'] = vcap_services['aws-elasticache-redis'][0]['credentials']['uri']
|
os.environ['REDIS_URL'] = vcap_services['aws-elasticache-redis'][0]['credentials']['uri'].replace('redis://','rediss://')
|
||||||
|
|||||||
18
manifest.yml
18
manifest.yml
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
applications:
|
applications:
|
||||||
- name: notifications-api
|
- name: notify-api-alt
|
||||||
buildpack: https://github.com/cloudfoundry/python-buildpack.git#v1.7.58
|
buildpack: https://github.com/cloudfoundry/python-buildpack.git#v1.7.58
|
||||||
instances: 1
|
instances: 1
|
||||||
memory: 1G
|
memory: 1G
|
||||||
@@ -9,22 +9,24 @@ applications:
|
|||||||
health-check-type: process
|
health-check-type: process
|
||||||
health-check-invocation-timeout: 1
|
health-check-invocation-timeout: 1
|
||||||
routes:
|
routes:
|
||||||
- route: notifications-api.app.cloud.gov
|
- route: notify-api-alt.app.cloud.gov
|
||||||
|
|
||||||
services:
|
services:
|
||||||
- api-psql
|
- api-alt-psql
|
||||||
- api-redis
|
- api-alt-redis
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
BP_PIP_VERSION: latest
|
||||||
NOTIFY_APP_NAME: api
|
NOTIFY_APP_NAME: api
|
||||||
NOTIFY_LOG_PATH: /home/vcap/logs/app.log
|
NOTIFY_LOG_PATH: /home/vcap/logs/app.log
|
||||||
FLASK_APP: application.py
|
FLASK_APP: application.py
|
||||||
FLASK_ENV: production
|
FLASK_ENV: production
|
||||||
|
|
||||||
NOTIFY_ENVIRONMENT: live
|
NOTIFY_ENVIRONMENT: live
|
||||||
API_HOST_NAME: https://notifications-api.app.cloud.gov
|
API_HOST_NAME: https://notify-api-alt.app.cloud.gov
|
||||||
ADMIN_BASE_URL: https://notifications-admin.app.cloud.gov
|
ADMIN_BASE_URL: https://notify-admin-alt.app.cloud.gov
|
||||||
NOTIFICATION_QUEUE_PREFIX: prototype_10x
|
NOTIFICATION_QUEUE_PREFIX: notify_alt_
|
||||||
|
REDIS_ENABLED: true
|
||||||
STATSD_HOST: localhost
|
STATSD_HOST: localhost
|
||||||
|
|
||||||
INTERNAL_CLIENT_API_KEYS: '{"notify-admin":["((ADMIN_CLIENT_SECRET))"]}'
|
INTERNAL_CLIENT_API_KEYS: '{"notify-admin":["((ADMIN_CLIENT_SECRET))"]}'
|
||||||
@@ -33,6 +35,8 @@ applications:
|
|||||||
ADMIN_CLIENT_SECRET: ((ADMIN_CLIENT_SECRET))
|
ADMIN_CLIENT_SECRET: ((ADMIN_CLIENT_SECRET))
|
||||||
DANGEROUS_SALT: ((DANGEROUS_SALT))
|
DANGEROUS_SALT: ((DANGEROUS_SALT))
|
||||||
SECRET_KEY: ((SECRET_KEY))
|
SECRET_KEY: ((SECRET_KEY))
|
||||||
|
AWS_ACCESS_KEY_ID: ((AWS_ACCESS_KEY_ID))
|
||||||
|
AWS_SECRET_ACCESS_KEY: ((AWS_SECRET_ACCESS_KEY))
|
||||||
AWS_REGION: us-west-2
|
AWS_REGION: us-west-2
|
||||||
AWS_PINPOINT_REGION: us-west-2
|
AWS_PINPOINT_REGION: us-west-2
|
||||||
AWS_US_TOLL_FREE_NUMBER: +18446120782
|
AWS_US_TOLL_FREE_NUMBER: +18446120782
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ celery[sqs]==5.2.6
|
|||||||
Flask-Bcrypt==1.0.1
|
Flask-Bcrypt==1.0.1
|
||||||
flask-marshmallow==0.14.0
|
flask-marshmallow==0.14.0
|
||||||
Flask-Migrate==3.1.0
|
Flask-Migrate==3.1.0
|
||||||
git+https://github.com/pallets-eco/flask-sqlalchemy.git@aa7a61a5357cf6f5dcc135d98c781192457aa6fa#egg=Flask-SQLAlchemy==2.5.1
|
Flask-SQLAlchemy==2.5.1
|
||||||
Flask==2.1.2
|
Flask==2.1.2
|
||||||
click-datetime==0.2
|
click-datetime==0.2
|
||||||
# Should be pinned until a new gunicorn release greater than 20.1.0 comes out. (Due to eventlet v0.33 compatibility issues)
|
# Should be pinned until a new gunicorn release greater than 20.1.0 comes out. (Due to eventlet v0.33 compatibility issues)
|
||||||
@@ -16,7 +16,6 @@ itsdangerous==2.1.2
|
|||||||
jsonschema[format]==4.5.1
|
jsonschema[format]==4.5.1
|
||||||
marshmallow-sqlalchemy==0.28.1
|
marshmallow-sqlalchemy==0.28.1
|
||||||
marshmallow==3.15.0
|
marshmallow==3.15.0
|
||||||
M2Crypto==0.38.0
|
|
||||||
psycopg2-binary==2.9.3
|
psycopg2-binary==2.9.3
|
||||||
PyJWT==2.4.0
|
PyJWT==2.4.0
|
||||||
SQLAlchemy==1.4.40
|
SQLAlchemy==1.4.40
|
||||||
@@ -27,6 +26,8 @@ defusedxml==0.7.1
|
|||||||
Werkzeug==2.1.1
|
Werkzeug==2.1.1
|
||||||
python-dotenv==0.20.0
|
python-dotenv==0.20.0
|
||||||
|
|
||||||
|
validatesns==0.1.1
|
||||||
|
|
||||||
notifications-python-client==6.3.0
|
notifications-python-client==6.3.0
|
||||||
|
|
||||||
# PaaS
|
# PaaS
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ amqp==5.1.1
|
|||||||
# via kombu
|
# via kombu
|
||||||
arrow==1.2.2
|
arrow==1.2.2
|
||||||
# via isoduration
|
# via isoduration
|
||||||
|
asn1crypto==1.5.1
|
||||||
|
# via oscrypto
|
||||||
async-timeout==4.0.2
|
async-timeout==4.0.2
|
||||||
# via redis
|
# via redis
|
||||||
attrs==21.4.0
|
attrs==21.4.0
|
||||||
@@ -99,7 +101,7 @@ flask-migrate==3.1.0
|
|||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
flask-redis==0.4.0
|
flask-redis==0.4.0
|
||||||
# via notifications-utils
|
# via notifications-utils
|
||||||
flask-sqlalchemy @ git+https://github.com/pallets-eco/flask-sqlalchemy.git@aa7a61a5357cf6f5dcc135d98c781192457aa6fa
|
flask-sqlalchemy==2.5.1
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# flask-migrate
|
# flask-migrate
|
||||||
@@ -148,8 +150,6 @@ kombu==5.2.4
|
|||||||
# via celery
|
# via celery
|
||||||
lxml==4.9.1
|
lxml==4.9.1
|
||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
m2crypto==0.38.0
|
|
||||||
# via -r requirements.in
|
|
||||||
mako==1.2.0
|
mako==1.2.0
|
||||||
# via alembic
|
# via alembic
|
||||||
markupsafe==2.1.1
|
markupsafe==2.1.1
|
||||||
@@ -171,6 +171,8 @@ notifications-utils @ git+https://github.com/GSA/notifications-utils.git
|
|||||||
# via -r requirements.in
|
# via -r requirements.in
|
||||||
orderedset==2.0.3
|
orderedset==2.0.3
|
||||||
# via notifications-utils
|
# via notifications-utils
|
||||||
|
oscrypto==1.3.0
|
||||||
|
# via validatesns
|
||||||
packaging==21.3
|
packaging==21.3
|
||||||
# via
|
# via
|
||||||
# bleach
|
# bleach
|
||||||
@@ -249,6 +251,7 @@ six==1.16.0
|
|||||||
# flask-marshmallow
|
# flask-marshmallow
|
||||||
# python-dateutil
|
# python-dateutil
|
||||||
# rfc3339-validator
|
# rfc3339-validator
|
||||||
|
# validatesns
|
||||||
smartypants==2.0.1
|
smartypants==2.0.1
|
||||||
# via notifications-utils
|
# via notifications-utils
|
||||||
soupsieve==2.3.2.post1
|
soupsieve==2.3.2.post1
|
||||||
@@ -269,6 +272,8 @@ urllib3==1.26.9
|
|||||||
# via
|
# via
|
||||||
# botocore
|
# botocore
|
||||||
# requests
|
# requests
|
||||||
|
validatesns==0.1.1
|
||||||
|
# via -r requirements.in
|
||||||
vine==5.0.0
|
vine==5.0.0
|
||||||
# via
|
# via
|
||||||
# amqp
|
# amqp
|
||||||
|
|||||||
Reference in New Issue
Block a user