from flask import Flask, request from flask_sqlalchemy import SQLAlchemy from sqlalchemy import MetaData, event from sqlalchemy.engine import Engine from flask_migrate import Migrate import version from lib.cachebust import cachebust from flask_limiter import Limiter from lib.auth import get_viewer import os import mimetypes app = Flask(__name__) default_config = { "SQLALCHEMY_TRACK_MODIFICATIONS": False, "SQLALCHEMY_DATABASE_URI": "postgresql+psycopg2:///forget", "SECRET_KEY": "hunter2", "CELERY_BROKER": "redis://", "HTTPS": True, "SENTRY_CONFIG": {}, "RATELIMIT_STORAGE_URL": "redis://", "REPO_URL": "https://github.com/codl/forget", "COMMIT_URL": "https://github.com/codl/forget/commits/{hash}", } app.config.update(default_config) app.config.from_pyfile('config.py', True) metadata = MetaData(naming_convention={ "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", "ck": "ck_%(table_name)s_%(constraint_name)s", "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s", "pk": "pk_%(table_name)s" }) db = SQLAlchemy(app, metadata=metadata) migrate = Migrate(app, db) if 'SQLALCHEMY_LOG_QUERIES_TO' in app.config: @event.listens_for(Engine, 'after_cursor_execute', named=True) def log_queries(statement='', **kwargs): with open(app.config['SQLALCHEMY_LOG_QUERIES_TO'], 'a') as f: f.write(statement.replace('\n', ' ') + ';\n') os.sync() sentry = None if 'SENTRY_DSN' in app.config: from raven.contrib.flask import Sentry app.config['SENTRY_CONFIG']['release'] = version.version sentry = Sentry(app, dsn=app.config['SENTRY_DSN']) url_for = cachebust(app) @app.context_processor def inject_static(): def static(filename, **kwargs): return url_for('static', filename=filename, **kwargs) return {'st': static} def rate_limit_key(): viewer = get_viewer() if viewer: return viewer.id for address in request.access_route: if address != '127.0.0.1': print(address) return address return request.remote_addr limiter = Limiter(app, key_func=rate_limit_key) @app.after_request def install_security_headers(resp): csp = ("default-src 'none';" "img-src 'self' https:;" "style-src 'self' 'unsafe-inline';" "frame-ancestors 'none';" ) if 'SENTRY_DSN' in app.config: csp += "script-src 'self' https://cdn.ravenjs.com/;" csp += "connect-src 'self' https://sentry.io/;" else: csp += "script-src 'self';" csp += "connect-src 'self';" if 'CSP_REPORT_URI' in app.config: csp += "report-uri " + app.config.get('CSP_REPORT_URI') if app.config.get('HTTPS'): resp.headers.set('strict-transport-security', 'max-age={}'.format(60*60*24*365)) csp += "; upgrade-insecure-requests" resp.headers.set('Content-Security-Policy', csp) resp.headers.set('referrer-policy', 'no-referrer') resp.headers.set('x-content-type-options', 'nosniff') resp.headers.set('x-frame-options', 'DENY') resp.headers.set('x-xss-protection', '1') return resp mimetypes.add_type('image/webp', '.webp')