Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FS-1116 add logging and pip compile #11

Merged
merged 13 commits into from
Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
with:
python-version: 3.10.1
- name: install dependencies
run: python -m pip install --upgrade pip && python -m pip install -r requirements.txt
run: python -m pip install --upgrade pip && python -m pip install -r requirements-dev.txt
- name: run unit tests
run: python -m pip install pytest && python -m pytest .
env:
Expand Down Expand Up @@ -66,7 +66,7 @@ jobs:
with:
python-version: 3.10.1
- name: install dependencies
run: python -m pip install --upgrade pip && python -m pip install -r requirements.txt
run: python -m pip install --upgrade pip && python -m pip install -r requirements-dev.txt
- name: Bandit
run: bandit -r ./app
- name: Safety
Expand Down
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,17 @@ Clone the repository
.venv\Scripts\activate.bat

### Install dependencies

From the top-level directory enter the command to install pip and the dependencies of the project

python3 -m pip install --upgrade pip && pip install -r requirements.txt
python3 -m pip install --upgrade pip && pip install -r requirements-dev.txt

NOTE: requirements-dev.txt and requirements.txt are updated using [pip-tools pip-compile](https://github.com/jazzband/pip-tools)
To update requirements please manually add the dependencies in the .in files (not the requirements.txt files)
Then run:

pip-compile requirements.in

pip-compile requirements-dev.in

## How to use
1. Set-up an API KEY that requires to connect with the govuk-notify-service
Expand All @@ -48,12 +55,24 @@ From the top-level directory enter the command to install pip and the dependenci

Note: For unit (integration) testing, you also need to set this in `pytest.ini`

1. Enter the virtual environment as described above, then:
2. Enter the virtual environment as described above, then:

flask run
`flask run`

Note: This service is an internal service so it doesn't have the frontend.

# Run with Gunicorn

In deployed environments the service is run with gunicorn. You can run the service locally with gunicorn to test

First set the FLASK_ENV environment you wish to test eg:

export FLASK_ENV=dev

Then run gunicorn using the following command:

gunicorn wsgi:app -c run/gunicorn/local.py

## How to post data for notification service.

Go to relevant service. See example
Expand Down
40 changes: 7 additions & 33 deletions app/create_app.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,19 @@
from config import Config
from flask import Flask
from flask_compress import Compress
from flask_talisman import Talisman
from fsd_utils.logging import logging


def create_app() -> Flask:

# ---- SETUP STATIC URL PATH.
flask_app = Flask(__name__)
flask_app = Flask("Notification")

flask_app.config.from_object("config.Config")

# ---- SETUP SECURITY CONFIGURATION & CSRF PROTECTION.
csp = {
"default-src": "'self'",
"script-src": [
"'self'",
"'sha256-+6WnXIl4mbFTCARd8N3COQmT3bJJmo32N8q8ZSQAIcU='",
"'sha256-l1eTVSK8DTnK8+yloud7wZUqFrI0atVo6VlC6PJvYaQ='",
],
"img-src": ["data:", "'self'"],
}

hss = {
"Strict-Transport-Security": (
"max-age=31536000; includeSubDomains; preload"
),
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "SAMEORIGIN",
"X-XSS-Protection": "1; mode=block",
"Feature_Policy": (
"microphone 'none'; camera 'none'; geolocation 'none'"
),
}
# Initialise logging
logging.init_app(flask_app)

Compress(flask_app)
Talisman(
flask_app,
content_security_policy=csp,
strict_transport_security=hss,
force_https=False,
)
# Configure application security with Talisman
Talisman(flask_app, **Config.TALISMAN_SETTINGS)

# ---- SETUP GLOBAL CONSTANTS (to be accessed from the app).
@flask_app.context_processor
Expand Down
21 changes: 12 additions & 9 deletions config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
# flake8: noqa
import os
from os import environ

FLASK_ENV = os.environ.get("FLASK_ENV")
FLASK_ENV = environ.get("FLASK_ENV")
srh-sloan marked this conversation as resolved.
Show resolved Hide resolved

if not FLASK_ENV:
raise KeyError("FLASK_ENV does not exist in environ")

match FLASK_ENV:
harryyo marked this conversation as resolved.
Show resolved Hide resolved
case "development":
from config.environments.development import (
DevelopmentConfig as Config,
)
from config.envs.development import DevelopmentConfig as Config
case "dev":
from config.envs.dev import DevConfig as Config
case "test":
from config.envs.test import TestConfig as Config
case "unit_test":
from config.environments.unit_test import (
UnitTestConfig as Config,
)
from config.envs.unit_test import UnitTestConfig as Config
case "production":
from config.envs.production import ProductionConfig as Config
case _:
from config.environments.default import DefaultConfig as Config
from config.envs.default import DefaultConfig as Config


try:
Config.pretty_print()
Expand Down
19 changes: 0 additions & 19 deletions config/environments/default.py

This file was deleted.

9 changes: 0 additions & 9 deletions config/environments/development.py

This file was deleted.

9 changes: 0 additions & 9 deletions config/environments/unit_test.py

This file was deleted.

78 changes: 78 additions & 0 deletions config/envs/default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import logging
import os

from fsd_utils import configclass


@configclass
class DefaultConfig:

GOV_NOTIFY_API_KEY = os.environ.get(
"GOV_NOTIFY_API_KEY", "gov_notify_api_key"
)

MAGIC_LINK_TEMPLATE_ID = os.environ.get(
"MAGIC_LINK_TEMPLATE_ID", "02a6d48a-f227-4b9a-9dd7-9e0cf203c8a2"
)
APPLICATION_RECORD_TEMPLATE_ID = os.environ.get(
"APPLICATION_RECORD_TEMPLATE_ID",
"0ddadcb3-ebe7-44f9-90e6-80ff3b61e0cb",
)

# Logging
FSD_LOG_LEVEL = logging.WARNING

# Talisman Config
FORCE_HTTPS = True

# Content Security Policy
SECURE_CSP = {
"default-src": "'self'",
"script-src": [
"'self'",
"'sha256-+6WnXIl4mbFTCARd8N3COQmT3bJJmo32N8q8ZSQAIcU='",
"'sha256-l1eTVSK8DTnK8+yloud7wZUqFrI0atVo6VlC6PJvYaQ='",
],
"img-src": ["data:", "'self'"],
}

# Security headers and other policies
FSD_REFERRER_POLICY = "strict-origin-when-cross-origin"
FSD_SESSION_COOKIE_SAMESITE = "Lax"
FSD_PERMISSIONS_POLICY = {"interest-cohort": "()"}
FSD_DOCUMENT_POLICY = {}
FSD_FEATURE_POLICY = {
"microphone": "'bob'",
"camera": "'none'",
"geolocation": "'none'",
}

DENY = "DENY"
SAMEORIGIN = "SAMEORIGIN"
ALLOW_FROM = "ALLOW-FROM"
ONE_YEAR_IN_SECS = 31556926

TALISMAN_SETTINGS = {
"feature_policy": FSD_FEATURE_POLICY,
"permissions_policy": FSD_PERMISSIONS_POLICY,
"document_policy": FSD_DOCUMENT_POLICY,
"force_https": FORCE_HTTPS,
"force_https_permanent": False,
"force_file_save": False,
"frame_options": "SAMEORIGIN",
"frame_options_allow_from": None,
"strict_transport_security": True,
"strict_transport_security_preload": True,
"strict_transport_security_max_age": ONE_YEAR_IN_SECS,
"strict_transport_security_include_subdomains": True,
"content_security_policy": SECURE_CSP,
"content_security_policy_report_uri": None,
"content_security_policy_report_only": False,
"content_security_policy_nonce_in": None,
"referrer_policy": FSD_REFERRER_POLICY,
"session_cookie_secure": True,
"session_cookie_http_only": True,
"session_cookie_samesite": FSD_SESSION_COOKIE_SAMESITE,
"x_content_type_options": True,
"x_xss_protection": True,
}
15 changes: 15 additions & 0 deletions config/envs/dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""Flask Dev Pipeline Environment Configuration."""
import logging

from config.envs.default import DefaultConfig as Config
from fsd_utils import configclass


@configclass
class DevConfig(Config):
# Application Config
SECRET_KEY = "dev"
SESSION_COOKIE_NAME = "session_cookie"

# Logging
FSD_LOG_LEVEL = logging.INFO
15 changes: 15 additions & 0 deletions config/envs/development.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import logging

from config.envs.default import DefaultConfig
from fsd_utils import configclass


@configclass
class DevelopmentConfig(DefaultConfig):
# Application Config
SECRET_KEY = "dev"
SESSION_COOKIE_NAME = "session_cookie"
FLASK_ENV = "development"

# Logging
FSD_LOG_LEVEL = logging.DEBUG
8 changes: 8 additions & 0 deletions config/envs/production.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Flask Production Environment Configuration."""
from config.envs.default import DefaultConfig as Config
from fsd_utils import configclass


@configclass
class ProductionConfig(Config):
pass
11 changes: 11 additions & 0 deletions config/envs/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Flask Test Environment Configuration."""
from os import environ

from config.envs.default import DefaultConfig as Config
from fsd_utils import configclass


@configclass
class TestConfig(Config):

SECRET_KEY = environ.get("SECRET_KEY", "test")
15 changes: 15 additions & 0 deletions config/envs/unit_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import logging

from config.envs.default import DefaultConfig
from fsd_utils import configclass


@configclass
class UnitTestConfig(DefaultConfig):

# Application Config
SECRET_KEY = "dev"
SESSION_COOKIE_NAME = "session_cookie"

# Logging
FSD_LOG_LEVEL = logging.DEBUG
9 changes: 6 additions & 3 deletions manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,31 @@ applications:
memory: 64M
buildpacks:
- https://github.com/cloudfoundry/python-buildpack.git
command: flask run --host 0.0.0.0 --port 8080
command: gunicorn wsgi:app -c run/gunicorn/devtest.py
routes:
- route: funding-service-design-notification-dev.apps.internal
env:
FLASK_ENV: dev
GOV_NOTIFY_API_KEY: ((GOV_NOTIFY_API_KEY))

- name: funding-service-design-notification-uat
memory: 64M
buildpacks:
- https://github.com/cloudfoundry/python-buildpack.git
command: flask run --host 0.0.0.0 --port 8080
command: gunicorn wsgi:app -c run/gunicorn/devtest.py
harryyo marked this conversation as resolved.
Show resolved Hide resolved
routes:
- route: funding-service-design-notification-uat.apps.internal
env:
FLASK_ENV: test
GOV_NOTIFY_API_KEY: ((GOV_NOTIFY_API_KEY))

- name: funding-service-design-notification-test
memory: 64M
buildpacks:
- https://github.com/cloudfoundry/python-buildpack.git
command: flask run --host 0.0.0.0 --port 8080
command: gunicorn wsgi:app -c run/gunicorn/devtest.py
routes:
- route: funding-service-design-notification-test.apps.internal
env:
FLASK_ENV: test
GOV_NOTIFY_API_KEY: ((GOV_NOTIFY_API_KEY))
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package]
name = "funding-service-design-notification"
version = "0.1.1"
description = "The funding service design notification service for the DLUHC."
authors = ["DLUHC"]
license = "MIT License"

[tool.black]
line-length = 79
experimental-string-processing = 1
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[pytest]
env =
FLASK_ENV=unit_test
FLASK_DEBUG=1
# To make the unit tests work, add the following env var with an appropriate value:
# GOV_NOTIFY_API_KEY=xxxx
# Without this set in the env you may see the following error:
Expand Down
Loading