Skip to content

Commit

Permalink
Merge pull request #360 from jwhitlock/regex_url_prefix
Browse files Browse the repository at this point in the history
Add regex patterns to OIDC_EXEMPT_URLS setting
  • Loading branch information
akatsoulas committed Aug 3, 2020
2 parents 0e0a5c3 + 709354c commit 8054836
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 5 deletions.
5 changes: 5 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ History

* Fix JWKS handling when the same `kid` value is used across JWKs with
different `alg` specified
* Support regex patterns in ``OIDC_EXEMPT_URLS``, to allow exempting session refreshes in
``SessionMiddleware`` for URLs matching the pattern
Thanks `@jwhitlock`_

.. _`@jwhitlock`: https://github.com/jwhitlock

1.2.3 (2020-01-02)
===================
Expand Down
6 changes: 3 additions & 3 deletions docs/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ of ``mozilla-django-oidc``.
:default: ``[]``

This is a list of absolute url paths or Django view names. This plus the
mozilla-django-oidc urls are exempted from the session renewal by the
``SessionRefresh`` middleware.
This is a list of absolute url paths, regular expressions for url paths, or
Django view names. This plus the mozilla-django-oidc urls are exempted from
the session renewal by the ``SessionRefresh`` middleware.

.. py:attribute:: OIDC_CREATE_USER
Expand Down
31 changes: 29 additions & 2 deletions mozilla_django_oidc/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@
# Python < 3
from urllib import urlencode

try:
# Python 3.7 or later
from re import Pattern as re_Pattern
except ImportError:
# Python 3.6 or earlier
from re import _pattern_type as re_Pattern


LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -47,7 +54,10 @@ def exempt_urls(self):
:returns: list of url paths (for example "/oidc/callback/")
"""
exempt_urls = list(self.get_settings('OIDC_EXEMPT_URLS', []))
exempt_urls = []
for url in self.get_settings('OIDC_EXEMPT_URLS', []):
if not isinstance(url, re_Pattern):
exempt_urls.append(url)
exempt_urls.extend([
'oidc_authentication_init',
'oidc_authentication_callback',
Expand All @@ -59,6 +69,22 @@ def exempt_urls(self):
for url in exempt_urls
])

@cached_property
def exempt_url_patterns(self):
"""Generate and return a set of url patterns to exempt from SessionRefresh
This takes the value of ``settings.OIDC_EXEMPT_URLS`` and returns the
values that are compiled regular expression patterns.
:returns: list of url patterns (for example,
``re.compile(r"/user/[0-9]+/image")``)
"""
exempt_patterns = set()
for url_pattern in self.get_settings('OIDC_EXEMPT_URLS', []):
if isinstance(url_pattern, re_Pattern):
exempt_patterns.add(url_pattern)
return exempt_patterns

def is_refreshable_url(self, request):
"""Takes a request and returns whether it triggers a refresh examination
Expand All @@ -78,7 +104,8 @@ def is_refreshable_url(self, request):
request.method == 'GET' and
request.user.is_authenticated and
is_oidc_enabled and
request.path not in self.exempt_urls
request.path not in self.exempt_urls and
not any(pat.match(request.path) for pat in self.exempt_url_patterns)
)

def process_request(self, request):
Expand Down
32 changes: 32 additions & 0 deletions tests/test_middleware.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
import re
import time

try:
Expand Down Expand Up @@ -232,6 +233,37 @@ def test_get_exempt_urls_setting_url_path(self):
[u'/authenticate/', u'/callback/', u'/foo/', u'/logout/']
)

def test_is_refreshable_url(self):
request = self.factory.get('/mdo_fake_view/')
request.user = self.user
request.session = dict()
middleware = SessionRefresh()
assert middleware.is_refreshable_url(request)

@override_settings(OIDC_EXEMPT_URLS=['mdo_fake_view'])
def test_is_not_refreshable_url_exempt_view_name(self):
request = self.factory.get('/mdo_fake_view/')
request.user = self.user
request.session = dict()
middleware = SessionRefresh()
assert not middleware.is_refreshable_url(request)

@override_settings(OIDC_EXEMPT_URLS=['/mdo_fake_view/'])
def test_is_not_refreshable_url_exempt_path(self):
request = self.factory.get('/mdo_fake_view/')
request.user = self.user
request.session = dict()
middleware = SessionRefresh()
assert not middleware.is_refreshable_url(request)

@override_settings(OIDC_EXEMPT_URLS=[re.compile(r'^/mdo_.*_view/$')])
def test_is_not_refreshable_url_exempt_pattern(self):
request = self.factory.get('/mdo_fake_view/')
request.user = self.user
request.session = dict()
middleware = SessionRefresh()
assert not middleware.is_refreshable_url(request)

def test_anonymous(self):
client = ClientWithUser()
resp = client.get('/mdo_fake_view/')
Expand Down

0 comments on commit 8054836

Please sign in to comment.