Determine value for op_browser_state from session_key or default

This commit is contained in:
Gertjan Oude Lohuis 2017-04-18 11:25:16 +02:00
parent 748a8bdfb8
commit 62a0a48678
6 changed files with 102 additions and 10 deletions

View file

@ -1,3 +1,5 @@
from hashlib import sha224
from django.core.urlresolvers import reverse
from django.http import HttpResponse
@ -50,6 +52,7 @@ def get_site_url(site_url=None, request=None):
'or set `SITE_URL` in settings, '
'or pass `request` object.')
def get_issuer(site_url=None, request=None):
"""
Construct the issuer full url. Basically is the site url with some path
@ -125,3 +128,11 @@ def default_idtoken_processing_hook(id_token, user):
:rtype dict
"""
return id_token
def get_browser_state_or_default(request):
"""
Determine value to use as session state.
"""
key = request.session.session_key or settings.get('OIDC_UNAUTHENTICATED_SESSION_MANAGEMENT_KEY')
return sha224(key.encode('utf-8')).hexdigest()

View file

@ -1,17 +1,17 @@
from hashlib import sha224
from django.conf import settings as django_settings
from django.utils.deprecation import MiddlewareMixin
from oidc_provider import settings
from oidc_provider.lib.utils.common import get_browser_state_or_default
class SessionManagementMiddleware(MiddlewareMixin):
"""
Maintain a `op_browser_state` cookie along with the `sessionid` cookie that
represents the End-User's login state at the OP. If the user is not logged
in then use `SECRET_KEY` value.
in then use the value of settings.OIDC_UNAUTHENTICATED_SESSION_MANAGEMENT_KEY.
"""
def process_response(self, request, response):
session_state = sha224((request.session.session_key or django_settings.SECRET_KEY).encode('utf-8')).hexdigest()
response.set_cookie('op_browser_state', session_state)
if settings.get('OIDC_SESSION_MANAGEMENT_ENABLE'):
response.set_cookie('op_browser_state', get_browser_state_or_default(request))
return response

View file

@ -1,4 +1,6 @@
import importlib
import random
import string
from django.conf import settings
@ -6,6 +8,9 @@ from django.conf import settings
class DefaultSettings(object):
required_attrs = ()
def __init__(self):
self._unauthenticated_session_management_key = None
@property
def OIDC_LOGIN_URL(self):
"""
@ -74,6 +79,18 @@ class DefaultSettings(object):
"""
return False
@property
def OIDC_UNAUTHENTICATED_SESSION_MANAGEMENT_KEY(self):
"""
OPTIONAL. Supply a fixed string to use as browser-state key for unauthenticated clients.
"""
# Memoize generated value
if not self._unauthenticated_session_management_key:
self._unauthenticated_session_management_key = ''.join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(100))
return self._unauthenticated_session_management_key
@property
def OIDC_SKIP_CONSENT_ALWAYS(self):
"""

View file

@ -0,0 +1,36 @@
from django.conf.urls import url
from django.test import TestCase, override_settings
from django.views import View
from mock import mock
class StubbedViews:
class SampleView(View):
pass
urlpatterns = [url('^test/', SampleView.as_view())]
@override_settings(ROOT_URLCONF=StubbedViews,
MIDDLEWARE=('django.contrib.sessions.middleware.SessionMiddleware',
'oidc_provider.middleware.SessionManagementMiddleware'),
OIDC_SESSION_MANAGEMENT_ENABLE=True)
class MiddlewareTestCase(TestCase):
def setUp(self):
patcher = mock.patch('oidc_provider.middleware.get_browser_state_or_default')
self.mock_get_state = patcher.start()
def test_session_management_middleware_sets_cookie_on_response(self):
response = self.client.get('/test/')
self.assertIn('op_browser_state', response.cookies)
self.assertEqual(response.cookies['op_browser_state'].value,
str(self.mock_get_state.return_value))
self.mock_get_state.assert_called_once_with(response.wsgi_request)
@override_settings(OIDC_SESSION_MANAGEMENT_ENABLE=False)
def test_session_management_middleware_does_not_set_cookie_if_session_management_disabled(self):
response = self.client.get('/test/')
self.assertNotIn('op_browser_state', response.cookies)

View file

@ -8,8 +8,18 @@ CUSTOM_TEMPLATES = {
}
class TokenTest(TestCase):
class SettingsTest(TestCase):
@override_settings(OIDC_TEMPLATES=CUSTOM_TEMPLATES)
def test_override_templates(self):
self.assertEqual(settings.get('OIDC_TEMPLATES'), CUSTOM_TEMPLATES)
def test_unauthenticated_session_management_key_has_default(self):
key = settings.get('OIDC_UNAUTHENTICATED_SESSION_MANAGEMENT_KEY')
self.assertRegexpMatches(key, r'[a-zA-Z0-9]+')
self.assertGreater(len(key), 50)
def test_unauthenticated_session_management_key_has_constant_value(self):
key1 = settings.get('OIDC_UNAUTHENTICATED_SESSION_MANAGEMENT_KEY')
key2 = settings.get('OIDC_UNAUTHENTICATED_SESSION_MANAGEMENT_KEY')
self.assertEqual(key1, key2)

View file

@ -1,13 +1,15 @@
import time
from datetime import datetime
from hashlib import sha224
from django.test import TestCase
from django.http import HttpRequest
from django.test import TestCase, override_settings
from django.utils import timezone
from mock import mock
from oidc_provider.lib.utils.common import get_issuer
from oidc_provider.lib.utils.common import get_issuer, get_browser_state_or_default
from oidc_provider.lib.utils.token import create_id_token
from oidc_provider.tests.app.utils import create_fake_user
from django.test import override_settings
class Request(object):
@ -78,3 +80,19 @@ class TokenTest(TestCase):
'iss': 'http://localhost:8000/openid',
'sub': str(self.user.id),
})
class BrowserStateTest(TestCase):
@override_settings(OIDC_UNAUTHENTICATED_SESSION_MANAGEMENT_KEY='my_static_key')
def test_get_browser_state_uses_value_from_settings_to_calculate_browser_state(self):
request = HttpRequest()
request.session = mock.Mock(session_key=None)
state = get_browser_state_or_default(request)
self.assertEqual(state, sha224('my_static_key'.encode('utf-8')).hexdigest())
def test_get_browser_state_uses_session_key_to_calculate_browser_state_if_available(self):
request = HttpRequest()
request.session = mock.Mock(session_key='my_session_key')
state = get_browser_state_or_default(request)
self.assertEqual(state, sha224('my_session_key'.encode('utf-8')).hexdigest())