-
-
Notifications
You must be signed in to change notification settings - Fork 239
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix 355 - Don't duplicate ? if qs in redirect url
Instead of appending a ? or # (implicit), parse the query string and fragment for parameters. Add our parameters and rebuild the redirect uri This should hopfully be more robust to redirect_uri's containing query parametes / fragment parameters One thing that it will not account for is if the fragment doesn't contain a parameter, but a single argument. That would be lost. But I'm not sure if you could legally combine those in a fragment? eg: https://example.com/#anchor¶m=value This also adds tests for AuthorizeError.create_uri and fixes updates the test_missing_nonce test to account for this change.
- Loading branch information
Showing
3 changed files
with
201 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
from django.test import TestCase | ||
from oidc_provider.lib.errors import AuthorizeError | ||
|
||
try: | ||
from urllib.parse import ( | ||
urlparse, | ||
parse_qsl | ||
) | ||
except ImportError: | ||
from urlparse import ( | ||
urlparse, | ||
parse_qsl | ||
) | ||
|
||
|
||
def compare(expected, created): | ||
"""Compare expected and created urls""" | ||
ex_parsed = list(urlparse(expected)) | ||
ex_qp = dict(parse_qsl(ex_parsed[4])) | ||
ex_frag = dict(parse_qsl(ex_parsed[5])) | ||
|
||
cr_parsed = list(urlparse(created)) | ||
cr_qp = dict(parse_qsl(cr_parsed[4])) | ||
cr_frag = dict(parse_qsl(cr_parsed[5])) | ||
|
||
# Validate scheme, netloc, path match | ||
assert ex_parsed[:3] == cr_parsed[:3] | ||
# Validate qp and frags match | ||
assert ex_qp == cr_qp | ||
assert ex_frag == cr_frag | ||
|
||
|
||
class TestImplicitAuthorizeErrorNonImplicit(TestCase): | ||
"""Tests with grant_type code - all responses in query params""" | ||
redirect_uri = 'https://example.com/' | ||
grant_type = 'code' | ||
error = 'login_required' | ||
desc = 'The+Authorization+Server+requires+End-User+authentication' | ||
|
||
def test_no_params(self): | ||
"""Test with a path only and no query/frag params""" | ||
redirect_uri = self.redirect_uri + 'path' | ||
error = AuthorizeError(redirect_uri, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri, '') | ||
expected_uri = '{}?error={}&error_description={}'.format( | ||
redirect_uri, self.error, self.desc) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_query_params_only(self): | ||
"""Test with query param in redirect uri""" | ||
redirect_uri = self.redirect_uri + "path/?action=something" | ||
error = AuthorizeError(redirect_uri, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri, '') | ||
expected_uri = '{}&error={}&error_description={}'.format( | ||
redirect_uri, self.error, self.desc) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_frag_params_only(self): | ||
"""Test with fragment params only""" | ||
redirect_uri = self.redirect_uri + 'path' | ||
frag = '#action=something' | ||
error = AuthorizeError(redirect_uri + frag, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri + frag, '') | ||
expected_uri = '{}path?error={}&error_description={}{}'.format( | ||
self.redirect_uri, self.error, self.desc, frag) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_query_and_frag_params(self): | ||
"""Test with both qp's and fragment""" | ||
redirect_uri = self.redirect_uri + 'path?my_qp=test' | ||
frag = '#action=something' | ||
error = AuthorizeError(redirect_uri + frag, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri + frag, '') | ||
expected_uri = '{}path?my_qp=test&error={}&error_description={}{}' \ | ||
.format(self.redirect_uri, self.error, self.desc, frag) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_with_state(self): | ||
"""Test with state""" | ||
redirect_uri = self.redirect_uri + 'path' | ||
state = 'my_state' | ||
error = AuthorizeError(redirect_uri, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri, state) | ||
expected_uri = '{}path?error={}&error_description={}&state={}' \ | ||
.format(self.redirect_uri, self.error, self.desc, state) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_with_deep_link(self): | ||
"""Test with a non-http schema; deep link style (think slack://)""" | ||
redirect_uri = 'slack://example.com/path' | ||
state = 'my_state' | ||
error = AuthorizeError(redirect_uri, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri, state) | ||
expected_uri = '{}?error={}&error_description={}&state={}' \ | ||
.format(redirect_uri, self.error, self.desc, state) | ||
compare(expected_uri, created_uri) | ||
|
||
|
||
class TestImplicitAuthorizeErrorImplicit(TestCase): | ||
"""Tests with grant_type code - all responses in query params""" | ||
redirect_uri = 'https://example.com/' | ||
grant_type = 'implicit' | ||
error = 'login_required' | ||
desc = 'The+Authorization+Server+requires+End-User+authentication' | ||
|
||
def test_no_params(self): | ||
"""Test with a path only and no query/frag params""" | ||
redirect_uri = self.redirect_uri + 'path' | ||
error = AuthorizeError(redirect_uri, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri, '') | ||
expected_uri = '{}#error={}&error_description={}'.format( | ||
redirect_uri, self.error, self.desc) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_query_params_only(self): | ||
"""Test with query param in redirect uri""" | ||
redirect_uri = self.redirect_uri + "path/?action=something" | ||
error = AuthorizeError(redirect_uri, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri, '') | ||
expected_uri = '{}#error={}&error_description={}'.format( | ||
redirect_uri, self.error, self.desc) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_frag_params_only(self): | ||
"""Test with fragment params only""" | ||
redirect_uri = self.redirect_uri + 'path' | ||
frag = '#action=something' | ||
error = AuthorizeError(redirect_uri + frag, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri + frag, '') | ||
expected_uri = '{}path{}&error={}&error_description={}'.format( | ||
self.redirect_uri, frag, self.error, self.desc) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_query_and_frag_params(self): | ||
"""Test with both qp's and fragment""" | ||
redirect_uri = self.redirect_uri + 'path?my_qp=test' | ||
frag = '#action=something' | ||
error = AuthorizeError(redirect_uri + frag, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri + frag, '') | ||
expected_uri = '{}path?my_qp=test{}&error={}&error_description={}' \ | ||
.format(self.redirect_uri, frag, self.error, self.desc) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_with_state(self): | ||
"""Test with state""" | ||
redirect_uri = self.redirect_uri + 'path' | ||
state = 'my_state' | ||
error = AuthorizeError(redirect_uri, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri, state) | ||
expected_uri = '{}path#error={}&error_description={}&state={}' \ | ||
.format(self.redirect_uri, self.error, self.desc, state) | ||
compare(expected_uri, created_uri) | ||
|
||
def test_with_deep_link(self): | ||
"""Test with a non-http schema; deep link style (think slack://)""" | ||
redirect_uri = 'slack://example.com/path' | ||
state = 'my_state' | ||
error = AuthorizeError(redirect_uri, self.error, self.grant_type) | ||
created_uri = error.create_uri(redirect_uri, state) | ||
expected_uri = '{}#error={}&error_description={}&state={}' \ | ||
.format(redirect_uri, self.error, self.desc, state) | ||
compare(expected_uri, created_uri) |