diff --git a/.circleci/config.yml b/.circleci/config.yml index 0e4a4b37a..028f6f411 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,9 +36,9 @@ gcloud_setup: &gcloud_setup run: name: setup gcloud command: | - # install + # install sudo curl https://dl.google.com/dl/cloudsdk/release/google-cloud-sdk.tar.gz > /tmp/google-cloud-sdk.tar.gz - sudo mkdir -p /usr/local/gcloud + sudo mkdir -p /usr/local/gcloud sudo tar -C /usr/local/gcloud -xvf /tmp/google-cloud-sdk.tar.gz sudo /usr/local/gcloud/google-cloud-sdk/install.sh --quiet echo PATH=$PATH:/usr/local/gcloud/google-cloud-sdk/bin >> ~/.bashrc @@ -190,7 +190,7 @@ jobs: command: | ./cc-test-reporter before-build . venv/bin/activate - coverage combine parallel-coverage/ + coverage combine parallel-coverage/ coverage xml coverage report ./cc-test-reporter format-coverage -o ./.coverage -t coverage.py @@ -221,18 +221,21 @@ jobs: echo "CELERY_BROKER_URL"=$(echo $IMAGE_CELERY_BROKER_URL_PRODUCTION) >> .env echo "CELERY_RESULT_BACKEND"=$(echo $IMAGE_CELERY_RESULT_BACKEND_PRODUCTION) >> .env echo "MRM_PUSH_URL"=$(echo $IMAGE_MRM_PUSH_URL_PRODUCTION) >> .env + echo "PROD_REQUEST_URL"=$(echo $IMAGE_PROD_REQUEST_URL) >> .env elif [ "$CIRCLE_BRANCH" == develop ]; then echo "DEV_DATABASE_URL"=$(echo $IMAGE_DEV_DATABASE_URL_STAGING) >> .env echo "DATABASE_URL"=$(echo $IMAGE_DATABASE_URL_STAGING) >> .env echo "CELERY_BROKER_URL"=$(echo $IMAGE_CELERY_BROKER_URL_STAGING) >> .env echo "CELERY_RESULT_BACKEND"=$(echo $IMAGE_CELERY_RESULT_BACKEND_STAGING) >> .env echo "MRM_PUSH_URL"=$(echo $IMAGE_MRM_PUSH_URL_STAGING) >> .env + echo "PROD_REQUEST_URL"=$(echo $IMAGE_PROD_REQUEST_URL) >> .env else echo "DEV_DATABASE_URL"=$(echo $IMAGE_DEV_DATABASE_URL_SANDBOX) >> .env echo "DATABASE_URL"=$(echo $IMAGE_DATABASE_URL_SANDBOX) >> .env echo "CELERY_BROKER_URL"=$(echo $IMAGE_CELERY_BROKER_URL_SANDBOX) >> .env echo "CELERY_RESULT_BACKEND"=$(echo $IMAGE_CELERY_RESULT_BACKEND_SANDBOX) >> .env echo "MRM_PUSH_URL"=$(echo $IMAGE_MRM_PUSH_URL_SANDBOX) >> .env + echo "PROD_REQUEST_URL"=$(echo $IMAGE_PROD_REQUEST_URL) >> .env fi echo "SECRET_KEY"=$(echo $IMAGE_SECRET_KEY) >> .env echo "MAIL_SERVER"=$(echo $IMAGE_MAIL_SERVER) >> .env @@ -301,13 +304,13 @@ jobs: command: | if [ "$CIRCLE_BRANCH" == master ] || [ "$CIRCLE_BRANCH" == develop ]; then touch google-service-key.json - echo $GOOGLE_CREDENTIALS_STAGING | base64 --decode >> google-service-key.json + echo $GOOGLE_CREDENTIALS_STAGING | base64 --decode >> google-service-key.json gcloud auth activate-service-account --key-file google-service-key.json gcloud --quiet config set project ${GOOGLE_PROJECT_ID_STAGING} gcloud --quiet config set compute/zone ${GOOGLE_COMPUTE_ZONE} else touch google-service-key.json - echo $GOOGLE_CREDENTIALS_SANDBOX | base64 --decode >> google-service-key.json + echo $GOOGLE_CREDENTIALS_SANDBOX | base64 --decode >> google-service-key.json gcloud auth activate-service-account --key-file google-service-key.json gcloud --quiet config set project ${GOOGLE_PROJECT_ID_SANDBOX} gcloud --quiet config set compute/zone ${GOOGLE_COMPUTE_ZONE} diff --git a/app.py b/app.py index f6ef5faea..ec8649fd4 100644 --- a/app.py +++ b/app.py @@ -1,15 +1,16 @@ +import os from flask import Flask, render_template - from flask_graphql import GraphQLView -from flask_cors import CORS from flask_json import FlaskJSON from flask_mail import Mail from config import config from helpers.database import db_session from schema import schema +from flask_cors import CORS from healthcheck_schema import healthcheck_schema from helpers.auth.authentication import Auth +from helpers.auth.allowed_requests import validate_origins from api.analytics.analytics_request import AnalyticsRequest mail = Mail() @@ -17,8 +18,13 @@ def create_app(config_name): app = Flask(__name__) - CORS(app) FlaskJSON(app) + if config_name == 'development' or 'testing': + CORS(app, resources={r"/mrm": {"origins": "*"}}) + if config_name == 'production': + CORS(app, resources={ + r"/mrm": {"origins": os.environ.get("PROD_REQUEST_URL").split(',')}}) # noqa 501 + app.config.from_object(config[config_name]) config[config_name].init_app(app) mail.init_app(app) @@ -35,6 +41,9 @@ def index(): graphiql=True # for having the GraphiQL interface ) ) + + app.before_request(validate_origins) + app.add_url_rule( '/_healthcheck', view_func=GraphQLView.as_view( diff --git a/helpers/auth/allowed_requests.py b/helpers/auth/allowed_requests.py new file mode 100644 index 000000000..545ca5522 --- /dev/null +++ b/helpers/auth/allowed_requests.py @@ -0,0 +1,15 @@ +import os +from flask import request, jsonify + + +def validate_origins(): + """validate requests for production environments""" + + user_agent = request.headers.get('User-Agent') + if(os.getenv('APP_SETTINGS') == 'production'): + if "insomnia" or "postman" in user_agent.lower(): + return jsonify( + { + 'message': + 'Invalid request. You are not allowed to make requests to this environment' # noqa 501 + }), 401 diff --git a/tests/test_authentication/test_allowed_requests.py b/tests/test_authentication/test_allowed_requests.py new file mode 100644 index 000000000..30ba4d557 --- /dev/null +++ b/tests/test_authentication/test_allowed_requests.py @@ -0,0 +1,15 @@ +from unittest.mock import patch, Mock +from tests.base import BaseTestCase +from helpers.auth.allowed_requests import validate_origins + + +class TestAllowedRequests(BaseTestCase): + @patch("helpers.auth.allowed_requests.request.headers.get", + Mock(return_value={'User-Agent': ['insomnia', 'postman']})) # noqa 501 + @patch("helpers.auth.allowed_requests.os.getenv", + Mock(return_value='production')) + def test_wrong_user_agent_in_production(self): + response = validate_origins() + self.assertIn( + b'Invalid request. You are not allowed to make requests to this environment', # noqa 501 + response[0].data)