Implemented pillar.flask_extra.ensure_schema(url)
This function ensures that the URL has the correct schema, given the app configuration. This is required because the Flask instance can sit behind an SSL-terminating proxy like HAProxy and not know that it is reachable via HTTPS.
This commit is contained in:
parent
15ce143356
commit
46beaece75
@ -27,3 +27,21 @@ def vary_xhr():
|
|||||||
return header_adder(f)
|
return header_adder(f)
|
||||||
|
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_schema(url: str) -> str:
|
||||||
|
"""Return the same URL with the configured PREFERRED_URL_SCHEME."""
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
if not url:
|
||||||
|
return url
|
||||||
|
|
||||||
|
bits = urllib.parse.urlsplit(url, allow_fragments=True)
|
||||||
|
|
||||||
|
if not bits[0] and not bits[1]:
|
||||||
|
# don't replace the schema if there is not even a hostname.
|
||||||
|
return url
|
||||||
|
|
||||||
|
scheme = flask.current_app.config.get('PREFERRED_URL_SCHEME', 'https')
|
||||||
|
bits = (scheme, *bits[1:])
|
||||||
|
return urllib.parse.urlunsplit(bits)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
from pillarsdk import Node
|
from pillarsdk import Node
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
@ -8,6 +9,7 @@ from flask import redirect
|
|||||||
from flask import request
|
from flask import request
|
||||||
from werkzeug.contrib.atom import AtomFeed
|
from werkzeug.contrib.atom import AtomFeed
|
||||||
|
|
||||||
|
from pillar.flask_extra import ensure_schema
|
||||||
from pillar.web.utils import system_util
|
from pillar.web.utils import system_util
|
||||||
from pillar.web.nodes.routes import url_for_node
|
from pillar.web.nodes.routes import url_for_node
|
||||||
from pillar.web.nodes.custom.posts import posts_view
|
from pillar.web.nodes.custom.posts import posts_view
|
||||||
@ -92,7 +94,8 @@ def feeds_blogs():
|
|||||||
@current_app.cache.cached(60*5)
|
@current_app.cache.cached(60*5)
|
||||||
def render_page():
|
def render_page():
|
||||||
feed = AtomFeed('Blender Cloud - Latest updates',
|
feed = AtomFeed('Blender Cloud - Latest updates',
|
||||||
feed_url=request.url, url=request.url_root)
|
feed_url=ensure_schema(request.url),
|
||||||
|
url=ensure_schema(request.url_root))
|
||||||
# Get latest blog posts
|
# Get latest blog posts
|
||||||
api = system_util.pillar_api()
|
api = system_util.pillar_api()
|
||||||
latest_posts = Node.all({
|
latest_posts = Node.all({
|
||||||
@ -106,9 +109,9 @@ def feeds_blogs():
|
|||||||
|
|
||||||
# Populate the feed
|
# Populate the feed
|
||||||
for post in latest_posts._items:
|
for post in latest_posts._items:
|
||||||
author = post.user.fullname
|
author = post.user.fullname or post.user.username
|
||||||
updated = post._updated if post._updated else post._created
|
updated = post._updated if post._updated else post._created
|
||||||
url = url_for_node(node=post)
|
url = ensure_schema(urllib.parse.urljoin(request.host_url, url_for_node(node=post)))
|
||||||
content = post.properties.content[:500]
|
content = post.properties.content[:500]
|
||||||
content = '<p>{0}... <a href="{1}">Read more</a></p>'.format(content, url)
|
content = '<p>{0}... <a href="{1}">Read more</a></p>'.format(content, url)
|
||||||
|
|
||||||
|
@ -33,3 +33,54 @@ class FlaskExtraTest(unittest.TestCase):
|
|||||||
self.assertEqual(201, resp.status_code)
|
self.assertEqual(201, resp.status_code)
|
||||||
self.assertNotIn('Vary', resp.headers)
|
self.assertNotIn('Vary', resp.headers)
|
||||||
self.assertEqual('nah', resp.data.decode())
|
self.assertEqual('nah', resp.data.decode())
|
||||||
|
|
||||||
|
|
||||||
|
class EnsureSchemaTest(unittest.TestCase):
|
||||||
|
def test_ensure_schema_http(self):
|
||||||
|
import pillar.flask_extra
|
||||||
|
|
||||||
|
suffix = '://user:password@hostname/some-path/%2Fpaththing?query=abc#fragment'
|
||||||
|
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
app.config['PREFERRED_URL_SCHEME'] = 'http'
|
||||||
|
with app.app_context():
|
||||||
|
for scheme in ('http', 'https', 'ftp', 'gopher'):
|
||||||
|
self.assertEqual(
|
||||||
|
f'http{suffix}',
|
||||||
|
pillar.flask_extra.ensure_schema(f'{scheme}{suffix}'))
|
||||||
|
|
||||||
|
def test_ensure_schema_https(self):
|
||||||
|
import pillar.flask_extra
|
||||||
|
|
||||||
|
suffix = '://user:password@hostname/some-path/%2Fpaththing?query=abc#fragment'
|
||||||
|
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
app.config['PREFERRED_URL_SCHEME'] = 'https'
|
||||||
|
with app.app_context():
|
||||||
|
for scheme in ('http', 'https', 'ftp', 'gopher'):
|
||||||
|
self.assertEqual(
|
||||||
|
f'https{suffix}',
|
||||||
|
pillar.flask_extra.ensure_schema(f'{scheme}{suffix}'))
|
||||||
|
|
||||||
|
def test_no_config(self):
|
||||||
|
import pillar.flask_extra
|
||||||
|
|
||||||
|
suffix = '://user:password@hostname/some-path/%2Fpaththing?query=abc#fragment'
|
||||||
|
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
app.config.pop('PREFERRED_URL_SCHEME', None)
|
||||||
|
with app.app_context():
|
||||||
|
self.assertEqual(
|
||||||
|
f'https{suffix}',
|
||||||
|
pillar.flask_extra.ensure_schema(f'gopher{suffix}'))
|
||||||
|
|
||||||
|
def test_corner_cases(self):
|
||||||
|
import pillar.flask_extra
|
||||||
|
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
app.config['PREFERRED_URL_SCHEME'] = 'https'
|
||||||
|
with app.app_context():
|
||||||
|
self.assertEqual('', pillar.flask_extra.ensure_schema(''))
|
||||||
|
self.assertEqual('/some/path/only', pillar.flask_extra.ensure_schema('/some/path/only'))
|
||||||
|
self.assertEqual('https://hostname/path',
|
||||||
|
pillar.flask_extra.ensure_schema('//hostname/path'))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user