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

Deprecate server_port from request data dictionary #276

Merged
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
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,6 @@ This parameter has the following scheme:
req = {
"http_host": "",
"script_name": "",
"server_port": "",
"get_data": "",
"post_data": "",

Expand All @@ -594,16 +593,14 @@ def prepare_from_django_request(request):
return {
'http_host': request.META['HTTP_HOST'],
'script_name': request.META['PATH_INFO'],
'server_port': request.META['SERVER_PORT'],
'get_data': request.GET.copy(),
'post_data': request.POST.copy()
}

def prepare_from_flask_request(request):
url_data = urlparse(request.url)
return {
'http_host': request.host,
'server_port': url_data.port,
'http_host': request.netloc,
'script_name': request.path,
'get_data': request.args.copy(),
'post_data': request.form.copy()
Expand Down
1 change: 0 additions & 1 deletion demo-django/demo/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ def prepare_django_request(request):
'https': 'on' if request.is_secure() else 'off',
'http_host': request.META['HTTP_HOST'],
'script_name': request.META['PATH_INFO'],
'server_port': request.META['SERVER_PORT'],
'get_data': request.GET.copy(),
# Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144
# 'lowercase_urlencoding': True,
Expand Down
4 changes: 1 addition & 3 deletions demo-flask/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@ def init_saml_auth(req):

def prepare_flask_request(request):
# If server is behind proxys or balancers use the HTTP_X_FORWARDED fields
url_data = urlparse(request.url)
return {
'https': 'on' if request.scheme == 'https' else 'off',
'http_host': request.host,
'server_port': url_data.port,
'http_host': request.netloc,
'script_name': request.path,
'get_data': request.args.copy(),
# Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144
Expand Down
3 changes: 1 addition & 2 deletions demo-tornado/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,8 @@ def prepare_tornado_request(request):

result = {
'https': 'on' if request == 'https' else 'off',
'http_host': tornado.httputil.split_host_and_port(request.host)[0],
'http_host': request.host,
'script_name': request.path,
'server_port': tornado.httputil.split_host_and_port(request.host)[1],
'get_data': dataDict,
'post_data': dataDict,
'query_string': request.query
Expand Down
5 changes: 1 addition & 4 deletions demo_pyramid/demo_pyramid/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,16 @@ def init_saml_auth(req):


def prepare_pyramid_request(request):
# Uncomment this portion to set the request.scheme and request.server_port
# Uncomment this portion to set the request.scheme
# based on the supplied `X-Forwarded` headers.
# Useful for running behind reverse proxies or balancers.
#
# if 'X-Forwarded-Proto' in request.headers:
# request.scheme = request.headers['X-Forwarded-Proto']
# if 'X-Forwarded-Port' in request.headers:
# request.server_port = int(request.headers['X-Forwarded-Port'])

return {
'https': 'on' if request.scheme == 'https' else 'off',
'http_host': request.host,
'server_port': request.server_port,
'script_name': request.path,
'get_data': request.GET.copy(),
# Uncomment if using ADFS as IdP, https://github.com/onelogin/python-saml/pull/144
Expand Down
49 changes: 19 additions & 30 deletions src/onelogin/saml2/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"""

import base64
import warnings
from copy import deepcopy
import calendar
from datetime import datetime
Expand Down Expand Up @@ -254,27 +255,25 @@ def get_self_url_host(request_data):
:rtype: string
"""
current_host = OneLogin_Saml2_Utils.get_self_host(request_data)
port = ''
if OneLogin_Saml2_Utils.is_https(request_data):
protocol = 'https'
else:
protocol = 'http'

if 'server_port' in request_data and request_data['server_port'] is not None:
port_number = str(request_data['server_port'])
port = ':' + port_number
protocol = 'https' if OneLogin_Saml2_Utils.is_https(request_data) else 'http'

if protocol == 'http' and port_number == '80':
port = ''
elif protocol == 'https' and port_number == '443':
port = ''
if request_data.get('server_port') is not None:
warnings.warn(
'The server_port key in request data is deprecated. '
'The http_host key should include a port, if required.',
category=DeprecationWarning,
)
port_suffix = ':%s' % request_data['server_port']
if not current_host.endswith(port_suffix):
if not ((protocol == 'https' and port_suffix == ':443') or (protocol == 'http' and port_suffix == ':80')):
current_host += port_suffix

return '%s://%s%s' % (protocol, current_host, port)
return '%s://%s' % (protocol, current_host)

@staticmethod
def get_self_host(request_data):
"""
Returns the current host.
Returns the current host (which may include a port number part).

:param request_data: The request as a dict
:type: dict
Expand All @@ -283,22 +282,11 @@ def get_self_host(request_data):
:rtype: string
"""
if 'http_host' in request_data:
current_host = request_data['http_host']
return request_data['http_host']
elif 'server_name' in request_data:
current_host = request_data['server_name']
else:
raise Exception('No hostname defined')

if ':' in current_host:
current_host_data = current_host.split(':')
possible_port = current_host_data[-1]
try:
int(possible_port)
current_host = current_host_data[0]
except ValueError:
current_host = ':'.join(current_host_data)

return current_host
warnings.warn("The server_name key in request data is undocumented & deprecated.", category=DeprecationWarning)
return request_data['server_name']
raise Exception('No hostname defined')

@staticmethod
def is_https(request_data):
Expand All @@ -312,6 +300,7 @@ def is_https(request_data):
:rtype: boolean
"""
is_https = 'https' in request_data and request_data['https'] != 'off'
# TODO: this use of server_port should be removed too
is_https = is_https or ('server_port' in request_data and str(request_data['server_port']) == '443')
return is_https

Expand Down
24 changes: 17 additions & 7 deletions tests/src/OneLogin/saml2_tests/response_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# MIT License

from base64 import b64decode

from lxml import etree
from datetime import datetime
from datetime import timedelta
Expand Down Expand Up @@ -1528,22 +1529,31 @@ def testIsInValidEncIssues(self):
self.assertFalse(response_5.is_valid(request_data))
self.assertEqual('The NameID of the Response is not encrypted and the SP require it', response_5.get_error())

def testIsInValidEncIssues_2(self):
settings_info_2 = self.loadSettingsJSON('settings3.json')
settings_info_2['strict'] = True
settings_info_2['security']['wantNameIdEncrypted'] = True
settings_2 = OneLogin_Saml2_Settings(settings_info_2)

request_data = {
'http_host': 'pytoolkit.com',
'server_port': 8000,
'script_name': '',
'request_uri': '?acs',
}

message_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion_encrypted_nameid.xml.base64'))
response_6 = OneLogin_Saml2_Response(settings_2, message_2)
self.assertFalse(response_6.is_valid(request_data))
self.assertEqual('The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response', response_6.get_error())
for separate_port in (False, True):
if separate_port:
request_data.update({
'http_host': 'pytoolkit.com',
'server_port': 8000,
})
else:
request_data.update({
'http_host': 'pytoolkit.com:8000',
})

message_2 = self.file_contents(join(self.data_path, 'responses', 'valid_encrypted_assertion_encrypted_nameid.xml.base64'))
response_6 = OneLogin_Saml2_Response(settings_2, message_2)
self.assertFalse(response_6.is_valid(request_data))
self.assertEqual('The attributes have expired, based on the SessionNotOnOrAfter of the AttributeStatement of this Response', response_6.get_error())

def testIsInValidCert(self):
"""
Expand Down
7 changes: 1 addition & 6 deletions tests/src/OneLogin/saml2_tests/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def testGetselfhost(self):
request_data = {
'http_host': 'example.com:443'
}
self.assertEqual('example.com', OneLogin_Saml2_Utils.get_self_host(request_data))
self.assertEqual('example.com:443', OneLogin_Saml2_Utils.get_self_host(request_data))

request_data = {
'http_host': 'example.com:ok'
Expand All @@ -211,11 +211,6 @@ def testisHTTPS(self):
}
self.assertTrue(OneLogin_Saml2_Utils.is_https(request_data))

request_data = {
'server_port': '80'
}
self.assertFalse(OneLogin_Saml2_Utils.is_https(request_data))

request_data = {
'server_port': '443'
}
Expand Down