Merge pull request #32 from tonyseek/app-factory-support

This commit is contained in:
Projjol Banerji
2019-02-16 11:44:18 +05:30
committed by GitHub
5 changed files with 194 additions and 14 deletions
+61
View File
@@ -0,0 +1,61 @@
# Created by https://www.gitignore.io
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
+31 -14
View File
@@ -2,32 +2,48 @@
from flask import request, redirect, current_app from flask import request, redirect, current_app
YEAR_IN_SECS = 31536000 YEAR_IN_SECS = 31536000
class SSLify(object): class SSLify(object):
"""Secures your Flask App.""" """Secures your Flask App."""
def __init__(self, app=None, age=YEAR_IN_SECS, subdomains=False, permanent=False, skips=None): def __init__(self, app=None, age=YEAR_IN_SECS, subdomains=False,
permanent=False, skips=None):
self.app = app or current_app self.app = app or current_app
self.hsts_age = age self.defaults = {
'subdomains': subdomains,
self.hsts_include_subdomains = subdomains 'permanent': permanent,
self.permanent = permanent 'skips': skips,
self.skip_list = skips 'age': age,
}
if app is not None: if app is not None:
self.init_app(app) self.init_app(app)
@property
def hsts_age(self):
return self.app.config['SSLIFY_AGE']
@property
def hsts_include_subdomains(self):
return self.app.config['SSLIFY_SUBDOMAINS']
@property
def permanent(self):
return self.app.config['SSLIFY_PERMANENT']
@property
def skip_list(self):
return self.app.config['SSLIFY_SKIPS']
def init_app(self, app): def init_app(self, app):
"""Configures the specified Flask app to enforce SSL.""" """Configures the specified Flask app to enforce SSL."""
app.config.setdefault('SSLIFY_SUBDOMAINS', False) app.config.setdefault('SSLIFY_AGE', self.defaults['age'])
app.config.setdefault('SSLIFY_PERMANENT', False) app.config.setdefault('SSLIFY_SUBDOMAINS', self.defaults['subdomains'])
app.config.setdefault('SSLIFY_SKIPS', None) app.config.setdefault('SSLIFY_PERMANENT', self.defaults['permanent'])
app.config.setdefault('SSLIFY_SKIPS', self.defaults['skips'])
self.hsts_include_subdomains = self.hsts_include_subdomains or app.config['SSLIFY_SUBDOMAINS']
self.permanent = self.permanent or self.app.config['SSLIFY_PERMANENT']
self.skip_list = self.skip_list or self.app.config['SSLIFY_SKIPS']
app.before_request(self.redirect_to_ssl) app.before_request(self.redirect_to_ssl)
app.after_request(self.set_hsts_header) app.after_request(self.set_hsts_header)
@@ -75,5 +91,6 @@ class SSLify(object):
"""Adds HSTS header to each response.""" """Adds HSTS header to each response."""
# Should we add STS header? # Should we add STS header?
if request.is_secure and not self.skip: if request.is_secure and not self.skip:
response.headers.setdefault('Strict-Transport-Security', self.hsts_header) response.headers.setdefault(
'Strict-Transport-Security', self.hsts_header)
return response return response
View File
+63
View File
@@ -0,0 +1,63 @@
from flask import Flask
from flask_sslify import SSLify
from pytest import fixture
class AppFactoryContext(object):
def __init__(self):
self.sslify = SSLify()
self.app = None
self.appctx = None
def __enter__(self):
self.app = self.create_app()
self.appctx = self.app.app_context()
self.appctx.push()
return self.appctx
def __exit__(self, exc_type, exc_value, exc_tb):
self.appctx.pop()
self.app = None
self.appctx = None
def create_app(self):
app = Flask(__name__)
app.config['DEBUG'] = False
app.config['TESTING'] = False
app.config['SERVER_NAME'] = 'example.com'
app.config['SSLIFY_PERMANENT'] = True
self.sslify.init_app(app)
app.add_url_rule('/', 'home', self.view_home)
return app
def view_home(self):
return 'home'
@fixture
def app_factory():
context = AppFactoryContext()
with context:
yield context
def test_config(app_factory):
assert app_factory.sslify.hsts_include_subdomains is False
assert app_factory.sslify.permanent is True
assert app_factory.sslify.skip_list is None
def test_redirection(app_factory):
client = app_factory.app.test_client()
r = client.get('/')
assert r.status_code == 301
assert r.headers['Location'] == 'https://example.com/'
def test_hsts_header(app_factory):
client = app_factory.app.test_client()
r = client.get('/', base_url='https://example.com')
assert r.status_code == 200
assert r.data.decode('utf-8') == 'home'
assert r.headers['Strict-Transport-Security'] == 'max-age=31536000'
+39
View File
@@ -0,0 +1,39 @@
from flask import Flask
from flask_sslify import SSLify
from pytest import fixture
@fixture
def sslify():
app = Flask(__name__)
app.config['DEBUG'] = False
app.config['TESTING'] = False
app.config['SERVER_NAME'] = 'example.com'
sslify = SSLify(app)
@app.route('/')
def home():
return 'home'
return sslify
def test_default_config(sslify):
assert sslify.hsts_include_subdomains is False
assert sslify.permanent is False
assert sslify.skip_list is None
def test_redirection(sslify):
client = sslify.app.test_client()
r = client.get('/')
assert r.status_code == 302
assert r.headers['Location'] == 'https://example.com/'
def test_hsts_header(sslify):
client = sslify.app.test_client()
r = client.get('/', base_url='https://example.com')
assert r.status_code == 200
assert r.data.decode('utf-8') == 'home'
assert r.headers['Strict-Transport-Security'] == 'max-age=31536000'