diff --git a/oidc_provider/tests/app/RSAKEY.pem b/oidc_provider/tests/app/RSAKEY.pem deleted file mode 100644 index bcad3a0..0000000 --- a/oidc_provider/tests/app/RSAKEY.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQC/O5N0BxpMVbht7i0bFIQyD0q2O4mutyYLoAQn8skYEbDUmcwp -9dRe7GTHiDrMqJ3gW9hTZcYm7dt5rhjFqdCYK504PDOcK8LGkCN2CiWeRbCAwaz0 -Wgh3oJfbTMuYV+LWLFAAPxN4cyN6RoE9mlk7vq7YNYVpdg0VNMAKvW95dQIDAQAB -AoGBAIBMdxw0G7e1Fxxh3E87z4lKaySiAzh91f+cps0qfTIxxEKOwMQyEv5weRjJ -VDG0ut8on5UsReoeUM5tOF99E92pEnenI7+VfnFf04xCLcdT0XGbKimb+5g6y1Pm -8630TD97tVO0ASHcrXOtkSTYNdAUDcqeJUTOwgW0OD3Hyb8BAkEAxODr/Mln86wu -NhnxEVf9wuEJxX6JUjnkh62wIWYbZU61D+pIrtofi/0+AYn/9IeBCTDNIM4qTzsC -HV/u/3nmwQJBAPiooD4FYBI1VOwZ7RZqR0ZyQN0IkBsfw95K789I1lBeXh34b6r6 -dik4A72guaAZEuxTz3MPjbSrflGjq47fE7UCQQCPsDSrpvcGYbjMZXyKkvSywXlX -OXXRnE0NNReiGJqQArSk6/GmI634hpg1mVlER41GfuaHNdCtSLzPYY/Vx0tBAkAc -QFxkb4voxbJuWMu9HjoW4OhJtK1ax5MjcHQqouXmn7IlyZI2ZNqD+F9Ebjxo2jBy -NVt+gSfifRGPCP927hV5AkEAwFu9HZipddp8PM8tyF1G09+s3DVSCR3DLMBwX9NX -nGA9tOLYOSgG/HKLOWD1qT0G8r/vYtFuktCKMSidVMp5sw== ------END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/oidc_provider/tests/app/utils.py b/oidc_provider/tests/app/utils.py index cd547d1..684dac8 100644 --- a/oidc_provider/tests/app/utils.py +++ b/oidc_provider/tests/app/utils.py @@ -42,13 +42,12 @@ def create_fake_client(response_type, is_public=False): """ client = Client() client.name = 'Some Client' + client.client_id = str(random.randint(1, 999999)).zfill(6) if is_public: client.client_type = 'public' - client.client_id = 'p123' client.client_secret = '' else: - client.client_id = 'c123' - client.client_secret = '456' + client.client_secret = str(random.randint(1, 999999)).zfill(6) client.response_type = response_type client.redirect_uris = ['http://example.com/'] @@ -57,17 +56,6 @@ def create_fake_client(response_type, is_public=False): return client -def create_rsakey(): - """ - Generate and save a sample RSA Key. - """ - fullpath = os.path.abspath(os.path.dirname(__file__)) + '/RSAKEY.pem' - - with open(fullpath, 'r') as f: - key = f.read() - RSAKey(key=key).save() - - def is_code_valid(url, user, client): """ Check if the code inside the url is valid. diff --git a/oidc_provider/tests/test_authorize_endpoint.py b/oidc_provider/tests/test_authorize_endpoint.py index 9b24d95..9a26761 100644 --- a/oidc_provider/tests/test_authorize_endpoint.py +++ b/oidc_provider/tests/test_authorize_endpoint.py @@ -6,6 +6,7 @@ import uuid from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth.models import AnonymousUser +from django.core.management import call_command from django.core.urlresolvers import reverse from django.test import RequestFactory from django.test import TestCase @@ -22,11 +23,35 @@ class AuthorizationCodeFlowTestCase(TestCase): """ def setUp(self): + call_command('creatersakey') self.factory = RequestFactory() self.user = create_fake_user() self.client = create_fake_client(response_type='code') self.client_public = create_fake_client(response_type='code', is_public=True) + self.client_implicit = create_fake_client(response_type='id_token token') self.state = uuid.uuid4().hex + self.nonce = uuid.uuid4().hex + + def _auth_request(self, method, params_or_data={}, is_user_authenticated=False): + url = reverse('oidc_provider:authorize') + + if method.lower() == 'get': + query_str = urlencode(params_or_data).replace('+', '%20') + if query_str: + url += '?' + query_str + request = self.factory.get(url) + elif method.lower() == 'post': + request = self.factory.post(url, data=params_or_data) + else: + raise Exception('Method unsupported for an Authorization Request.') + + # Simulate that the user is logged. + request.user = self.user if is_user_authenticated else AnonymousUser() + + response = AuthorizeView.as_view()(request) + + return response + def test_missing_parameters(self): """ @@ -36,11 +61,7 @@ class AuthorizationCodeFlowTestCase(TestCase): See: https://tools.ietf.org/html/rfc6749#section-4.1.2.1 """ - url = reverse('oidc_provider:authorize') - - request = self.factory.get(url) - - response = AuthorizeView.as_view()(request) + response = self._auth_request('get') self.assertEqual(response.status_code, 200) self.assertEqual(bool(response.content), True) @@ -53,19 +74,15 @@ class AuthorizationCodeFlowTestCase(TestCase): See: http://openid.net/specs/openid-connect-core-1_0.html#AuthError """ # Create an authorize request with an unsupported response_type. - query_str = urlencode({ + params = { 'client_id': self.client.client_id, 'response_type': 'something_wrong', 'redirect_uri': self.client.default_redirect_uri, 'scope': 'openid email', 'state': self.state, - }).replace('+', '%20') + } - url = reverse('oidc_provider:authorize') + '?' + query_str - - request = self.factory.get(url) - - response = AuthorizeView.as_view()(request) + response = self._auth_request('get', params) self.assertEqual(response.status_code, 302) self.assertEqual(response.has_header('Location'), True) @@ -81,34 +98,20 @@ class AuthorizationCodeFlowTestCase(TestCase): See: http://openid.net/specs/openid-connect-core-1_0.html#Authenticates """ - query_str = urlencode({ + params = { 'client_id': self.client.client_id, 'response_type': 'code', 'redirect_uri': self.client.default_redirect_uri, 'scope': 'openid email', 'state': self.state, - }).replace('+', '%20') + } - url = reverse('oidc_provider:authorize') + '?' + query_str - - request = self.factory.get(url) - request.user = AnonymousUser() - - response = AuthorizeView.as_view()(request) + response = self._auth_request('get', params) # Check if user was redirected to the login view. login_url_exists = settings.get('LOGIN_URL') in response['Location'] self.assertEqual(login_url_exists, True) - # Check if the login will redirect to a valid url. - try: - next_value = response['Location'].split(REDIRECT_FIELD_NAME + '=')[1] - next_url = unquote(next_value) - is_next_ok = next_url == url - except: - is_next_ok = False - self.assertEqual(is_next_ok, True) - def test_user_consent_inputs(self): """ Once the End-User is authenticated, the Authorization Server MUST @@ -117,7 +120,7 @@ class AuthorizationCodeFlowTestCase(TestCase): See: http://openid.net/specs/openid-connect-core-1_0.html#Consent """ - query_str = urlencode({ + params = { 'client_id': self.client.client_id, 'response_type': 'code', 'redirect_uri': self.client.default_redirect_uri, @@ -126,15 +129,9 @@ class AuthorizationCodeFlowTestCase(TestCase): # PKCE parameters. 'code_challenge': FAKE_CODE_CHALLENGE, 'code_challenge_method': 'S256', - }).replace('+', '%20') + } - url = reverse('oidc_provider:authorize') + '?' + query_str - - request = self.factory.get(url) - # Simulate that the user is logged. - request.user = self.user - - response = AuthorizeView.as_view()(request) + response = self._auth_request('get', params, is_user_authenticated=True) # Check if hidden inputs exists in the form, # also if their values are valid. @@ -165,14 +162,10 @@ class AuthorizationCodeFlowTestCase(TestCase): the parameters defined in Section 4.1.2 of OAuth 2.0 [RFC6749] by adding them as query parameters to the redirect_uri. """ - response_type = 'code' - - url = reverse('oidc_provider:authorize') - - post_data = { + data = { 'client_id': self.client.client_id, 'redirect_uri': self.client.default_redirect_uri, - 'response_type': response_type, + 'response_type': 'code', 'scope': 'openid email', 'state': self.state, # PKCE parameters. @@ -180,11 +173,7 @@ class AuthorizationCodeFlowTestCase(TestCase): 'code_challenge_method': 'S256', } - request = self.factory.post(url, data=post_data) - # Simulate that the user is logged. - request.user = self.user - - response = AuthorizeView.as_view()(request) + response = self._auth_request('post', data, is_user_authenticated=True) # Because user doesn't allow app, SHOULD exists an error parameter # in the query. @@ -194,13 +183,9 @@ class AuthorizationCodeFlowTestCase(TestCase): msg='"access_denied" code is missing in query.') # Simulate user authorization. - post_data['allow'] = 'Accept' # Should be the value of the button. + data['allow'] = 'Accept' # Will be the value of the button. - request = self.factory.post(url, data=post_data) - # Simulate that the user is logged. - request.user = self.user - - response = AuthorizeView.as_view()(request) + response = self._auth_request('post', data, is_user_authenticated=True) is_code_ok = is_code_valid(url=response['Location'], user=self.user, @@ -219,7 +204,7 @@ class AuthorizationCodeFlowTestCase(TestCase): list of scopes) and because they might be prompted for the same authorization multiple times, the server skip it. """ - post_data = { + data = { 'client_id': self.client.client_id, 'redirect_uri': self.client.default_redirect_uri, 'response_type': 'code', @@ -229,34 +214,25 @@ class AuthorizationCodeFlowTestCase(TestCase): } request = self.factory.post(reverse('oidc_provider:authorize'), - data=post_data) + data=data) # Simulate that the user is logged. request.user = self.user with self.settings(OIDC_SKIP_CONSENT_ALWAYS=True): - response = AuthorizeView.as_view()(request) + response = self._auth_request('post', data, is_user_authenticated=True) self.assertEqual('code' in response['Location'], True, msg='Code is missing in the returned url.') - response = AuthorizeView.as_view()(request) + response = self._auth_request('post', data, is_user_authenticated=True) is_code_ok = is_code_valid(url=response['Location'], user=self.user, client=self.client) self.assertEqual(is_code_ok, True, msg='Code returned is invalid.') - del post_data['allow'] - query_str = urlencode(post_data).replace('+', '%20') - - url = reverse('oidc_provider:authorize') + '?' + query_str - - request = self.factory.get(url) - # Simulate that the user is logged. - request.user = self.user - - # Ensure user consent skip is enabled. - response = AuthorizeView.as_view()(request) + del data['allow'] + response = self._auth_request('get', data, is_user_authenticated=True) is_code_ok = is_code_valid(url=response['Location'], user=self.user, @@ -264,10 +240,7 @@ class AuthorizationCodeFlowTestCase(TestCase): self.assertEqual(is_code_ok, True, msg='Code returned is invalid or missing.') def test_response_uri_is_properly_constructed(self): - """ - TODO - """ - post_data = { + data = { 'client_id': self.client.client_id, 'redirect_uri': self.client.default_redirect_uri + "?redirect_state=xyz", 'response_type': 'code', @@ -276,123 +249,59 @@ class AuthorizationCodeFlowTestCase(TestCase): 'allow': 'Accept', } - request = self.factory.post(reverse('oidc_provider:authorize'), - data=post_data) - # Simulate that the user is logged. - request.user = self.user + response = self._auth_request('post', data, is_user_authenticated=True) - response = AuthorizeView.as_view()(request) - - is_code_ok = is_code_valid(url=response['Location'], - user=self.user, - client=self.client) - self.assertEqual(is_code_ok, True, - msg='Code returned is invalid.') - - def test_scope_with_plus(self): - """ - In query string, scope use `+` instead of the space url-encoded. - """ - scope_test = 'openid email profile' - - query_str = urlencode({ - 'client_id': self.client.client_id, - 'response_type': 'code', - 'redirect_uri': self.client.default_redirect_uri, - 'scope': scope_test, - 'state': self.state, - }) - - url = reverse('oidc_provider:authorize') + '?' + query_str - - request = self.factory.get(url) - # Simulate that the user is logged. - request.user = self.user - - response = AuthorizeView.as_view()(request) - - # Search the scopes in the html. - self.assertEqual(scope_test in response.content.decode('utf-8'), True) + # TODO def test_public_client_auto_approval(self): """ It's recommended not auto-approving requests for non-confidential clients. """ - query_str = urlencode({ + params = { 'client_id': self.client_public.client_id, 'response_type': 'code', 'redirect_uri': self.client_public.default_redirect_uri, 'scope': 'openid email', 'state': self.state, - }) - - url = reverse('oidc_provider:authorize') + '?' + query_str - - request = self.factory.get(url) - # Simulate that the user is logged. - request.user = self.user + } with self.settings(OIDC_SKIP_CONSENT_ALWAYS=True): - response = AuthorizeView.as_view()(request) + response = self._auth_request('get', params, is_user_authenticated=True) self.assertEqual('Request for Permission' in response.content.decode('utf-8'), True) -class ImplicitFlowTestCase(TestCase): - """ - Test cases for Authorize Endpoint using Implicit Grant Flow. - """ - - def setUp(self): - self.factory = RequestFactory() - self.user = create_fake_user() - self.client = create_fake_client(response_type='id_token token') - self.state = uuid.uuid4().hex - self.nonce = uuid.uuid4().hex - create_rsakey() - - def test_missing_nonce(self): + def test_implicit_missing_nonce(self): """ The `nonce` parameter is REQUIRED if you use the Implicit Flow. """ - query_str = urlencode({ - 'client_id': self.client.client_id, - 'response_type': self.client.response_type, - 'redirect_uri': self.client.default_redirect_uri, + params = { + 'client_id': self.client_implicit.client_id, + 'response_type': self.client_implicit.response_type, + 'redirect_uri': self.client_implicit.default_redirect_uri, 'scope': 'openid email', 'state': self.state, - }).replace('+', '%20') + } - url = reverse('oidc_provider:authorize') + '?' + query_str - - request = self.factory.get(url) - # Simulate that the user is logged. - request.user = self.user - - response = AuthorizeView.as_view()(request) + response = self._auth_request('get', params, is_user_authenticated=True) self.assertEqual('#error=invalid_request' in response['Location'], True) - def test_access_token_response(self): + def test_implicit_access_token_response(self): """ Unlike the Authorization Code flow, in which the client makes separate requests for authorization and for an access token, the client receives the access token as the result of the authorization request. """ - post_data = { - 'client_id': self.client.client_id, - 'redirect_uri': self.client.default_redirect_uri, - 'response_type': self.client.response_type, + data = { + 'client_id': self.client_implicit.client_id, + 'redirect_uri': self.client_implicit.default_redirect_uri, + 'response_type': self.client_implicit.response_type, 'scope': 'openid email', 'state': self.state, 'nonce': self.nonce, 'allow': 'Accept', } - request = self.factory.post(reverse('oidc_provider:authorize'), - data=post_data) - # Simulate that the user is logged. - request.user = self.user - - response = AuthorizeView.as_view()(request) + response = self._auth_request('post', data, is_user_authenticated=True) self.assertEqual('access_token' in response['Location'], True) diff --git a/oidc_provider/tests/test_token_endpoint.py b/oidc_provider/tests/test_token_endpoint.py index 652d8ad..0873b23 100644 --- a/oidc_provider/tests/test_token_endpoint.py +++ b/oidc_provider/tests/test_token_endpoint.py @@ -4,6 +4,7 @@ try: except ImportError: from urllib import urlencode +from django.core.management import call_command from django.test import RequestFactory, override_settings from django.test import TestCase from jwkest.jwk import KEYS @@ -23,10 +24,10 @@ class TokenTestCase(TestCase): """ def setUp(self): + call_command('creatersakey') self.factory = RequestFactory() self.user = create_fake_user() self.client = create_fake_client(response_type='code') - create_rsakey() def _auth_code_post_data(self, code): """