Files
notifications-admin/app/__init__.py

186 lines
5.8 KiB
Python
Raw Normal View History

2015-11-24 09:40:14 +00:00
import os
import re
2016-01-29 14:41:07 +00:00
import ast
import dateutil
from flask import (Flask, session, Markup, escape, render_template, make_response)
2015-11-24 09:40:14 +00:00
from flask._compat import string_types
from flask.ext.sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_wtf import CsrfProtect
from werkzeug.exceptions import abort
from pygments import highlight
from pygments.lexers import JavascriptLexer
from pygments.formatters import HtmlFormatter
2016-01-15 15:15:35 +00:00
from app.notify_client.api_client import NotificationsAdminAPIClient
from app.notify_client.api_key_api_client import ApiKeyApiClient
from app.notify_client.user_api_client import UserApiClient
2016-01-29 10:27:23 +00:00
from app.notify_client.job_api_client import JobApiClient
2016-01-29 14:41:07 +00:00
from app.notify_client.status_api_client import StatusApiClient
from app.its_dangerous_session import ItsdangerousSessionInterface
from app.asset_fingerprinter import AssetFingerprinter
from app.utils import validate_phone_number, InvalidPhoneError
2015-11-30 14:32:58 +00:00
import app.proxy_fix
from config import configs
2016-01-07 15:39:36 +00:00
from utils import logging
2015-11-30 14:32:58 +00:00
login_manager = LoginManager()
csrf = CsrfProtect()
2015-11-20 16:22:44 +00:00
2016-01-15 15:15:35 +00:00
notifications_api_client = NotificationsAdminAPIClient()
user_api_client = UserApiClient()
api_key_api_client = ApiKeyApiClient()
2016-01-29 10:27:23 +00:00
job_api_client = JobApiClient()
2016-01-29 14:41:07 +00:00
status_api_client = StatusApiClient()
asset_fingerprinter = AssetFingerprinter()
2016-01-15 15:15:35 +00:00
2015-11-20 16:22:44 +00:00
def create_app(config_name, config_overrides=None):
2015-11-20 16:22:44 +00:00
application = Flask(__name__)
2016-01-07 16:24:10 +00:00
application.config['NOTIFY_ADMIN_ENVIRONMENT'] = config_name
2015-11-24 09:40:14 +00:00
application.config.from_object(configs[config_name])
init_app(application, config_overrides)
2016-01-07 15:39:36 +00:00
logging.init_app(application)
init_csrf(application)
2016-01-15 15:15:35 +00:00
notifications_api_client.init_app(application)
user_api_client.init_app(application)
api_key_api_client.init_app(application)
2016-01-29 10:27:23 +00:00
job_api_client.init_app(application)
2016-01-29 14:41:07 +00:00
status_api_client.init_app(application)
2016-01-15 15:15:35 +00:00
login_manager.init_app(application)
2016-01-06 17:17:02 +00:00
login_manager.login_view = 'main.sign_in'
2015-11-24 09:40:14 +00:00
2015-11-20 16:33:11 +00:00
from app.main import main as main_blueprint
2015-11-20 16:22:44 +00:00
application.register_blueprint(main_blueprint)
from .status import status as status_blueprint
application.register_blueprint(status_blueprint)
2015-11-30 14:32:58 +00:00
proxy_fix.init_app(application)
application.session_interface = ItsdangerousSessionInterface()
2015-11-30 14:32:58 +00:00
application.add_template_filter(nl2br)
application.add_template_filter(format_datetime)
application.add_template_filter(syntax_highlight_json)
application.add_template_filter(valid_phone_number)
application.after_request(useful_headers_after_request)
2016-01-07 15:48:29 +00:00
register_errorhandlers(application)
2015-11-20 16:22:44 +00:00
return application
2015-11-24 09:40:14 +00:00
def init_csrf(application):
csrf.init_app(application)
@csrf.error_handler
def csrf_handler(reason):
if 'user_id' not in session:
application.logger.info(
u'csrf.session_expired: Redirecting user to log in page'
)
return application.login_manager.unauthorized()
application.logger.info(
u'csrf.invalid_token: Aborting request, user_id: {user_id}',
extra={'user_id': session['user_id']})
abort(400, reason)
def init_app(app, config_overrides):
2015-11-24 09:40:14 +00:00
if config_overrides:
for key in app.config.keys():
if key in config_overrides:
app.config[key] = config_overrides[key]
for key, value in app.config.items():
if key in os.environ:
app.config[key] = convert_to_boolean(os.environ[key])
@app.context_processor
def inject_global_template_variables():
return {
'asset_path': '/static/',
'header_colour': app.config['HEADER_COLOUR'],
'asset_url': asset_fingerprinter.get_url
}
2015-11-24 09:40:14 +00:00
def convert_to_boolean(value):
if isinstance(value, string_types):
if value.lower() in ['t', 'true', 'on', 'yes', '1']:
return True
elif value.lower() in ['f', 'false', 'off', 'no', '0']:
return False
return value
def nl2br(value):
_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', Markup('<br>\n'))
for p in _paragraph_re.split(escape(value)))
return Markup(result)
def syntax_highlight_json(code):
return Markup(highlight(code, JavascriptLexer(), HtmlFormatter(noclasses=True)))
def format_datetime(date):
date = dateutil.parser.parse(date)
native = date.replace(tzinfo=None)
return native.strftime('%A %d %B %Y at %H:%M')
def valid_phone_number(phone_number):
try:
validate_phone_number(phone_number)
return True
except InvalidPhoneError:
return False
# https://www.owasp.org/index.php/List_of_useful_HTTP_headers
def useful_headers_after_request(response):
response.headers.add('X-Frame-Options', 'deny')
response.headers.add('X-Content-Type-Options', 'nosniff')
response.headers.add('X-XSS-Protection', '1; mode=block')
response.headers.add('Content-Security-Policy',
"default-src 'self' 'unsafe-inline'; font-src 'self' data:;") # noqa
if 'Cache-Control' in response.headers:
del response.headers['Cache-Control']
response.headers.add(
'Cache-Control', 'no-store, no-cache, private, must-revalidate')
return response
2016-01-07 15:48:29 +00:00
2016-01-07 15:55:55 +00:00
def register_errorhandlers(application):
2016-01-07 15:48:29 +00:00
def render_error(error):
# If a HTTPException, pull the `code` attribute; default to 500
error_code = getattr(error, 'code', 500)
resp = make_response(render_template("error/{0}.html".format(error_code)), error_code)
return useful_headers_after_request(resp)
2016-01-07 15:48:29 +00:00
for errcode in [401, 404, 500]:
2016-01-07 15:55:55 +00:00
application.errorhandler(errcode)(render_error)
2016-01-29 14:41:07 +00:00
def get_app_version():
build = 'n/a'
build_time = "n/a"
try:
from app import version
build = version.__build__
build_time = version.__time__
except:
pass
return build, build_time