Commit 6e8bd685 by Xavier Antoviaque

Add support for excluding certain URL paths from redirect

To allow access to certain URLs without any auth or cookie, like
'/heartbeat'
parent b70f02ac
......@@ -27,6 +27,10 @@ class SplashMiddleware(object):
if not config.enabled:
return
# Some URLs should never be redirected
if request.path_info in config.unaffected_url_paths_list:
return
# Some users should never be redirected
if request.user.username in config.unaffected_usernames_list:
return
......
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'SplashConfig.unaffected_url_paths'
db.add_column('splash_splashconfig', 'unaffected_url_paths',
self.gf('django.db.models.fields.TextField')(default='/heartbeat', blank=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'SplashConfig.unaffected_url_paths'
db.delete_column('splash_splashconfig', 'unaffected_url_paths')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'splash.splashconfig': {
'Meta': {'object_name': 'SplashConfig'},
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
'cookie_allowed_values': ('django.db.models.fields.TextField', [], {'default': "'seen'"}),
'cookie_name': ('django.db.models.fields.TextField', [], {'default': "'edx_splash_screen'"}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'redirect_url': ('django.db.models.fields.URLField', [], {'default': "'http://edx.org'", 'max_length': '200'}),
'unaffected_url_paths': ('django.db.models.fields.TextField', [], {'default': "'/heartbeat'", 'blank': 'True'}),
'unaffected_usernames': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
}
}
complete_apps = ['splash']
\ No newline at end of file
......@@ -23,6 +23,11 @@ class SplashConfig(ConfigurationModel):
blank=True,
help_text="Comma-separated list of users which should never be redirected (usernames)"
)
unaffected_url_paths = models.TextField(
default='/heartbeat',
blank=True,
help_text="Comma-separated list of URL paths (not including the hostname) which should not be redirected"
)
redirect_url = models.URLField(
default='http://edx.org',
help_text="The URL the users should be redirected to when they don't have the right cookie"
......@@ -48,6 +53,16 @@ class SplashConfig(ConfigurationModel):
return [name.strip() for name in self.unaffected_usernames.split(',')] # pylint: disable=no-member
@property
def unaffected_url_paths_list(self):
"""
`unaffected_url_paths` as a list of URL paths values
"""
if not self.unaffected_url_paths.strip(): # pylint: disable=no-member
return []
return [url.strip() for url in self.unaffected_url_paths.split(',')] # pylint: disable=no-member
def save(self, *args, **kwargs):
"""Call `full_clean` before saving to ensure proper validation of configuration values"""
self.full_clean()
......
......@@ -26,11 +26,11 @@ class SplashMiddlewareTestCase(TestCase):
self.request_factory = RequestFactory(SERVER_NAME='example.org')
SplashConfig().save()
def build_request(self, username=None, cookies=None):
def build_request(self, username=None, cookies=None, url_path='/somewhere'):
"""
Builds a new request, associated with a user (anonymous by default)
"""
request = self.request_factory.get('/somewhere')
request = self.request_factory.get(url_path)
if username is None:
request.user = AnonymousUser()
......@@ -175,3 +175,28 @@ class SplashMiddlewareTestCase(TestCase):
"""
config = SplashConfig(redirect_url='/somewhere')
self.assertRaises(ValidationError, config.save)
def test_unaffected_path_default(self):
"""
Unaffected paths should never be redirected - default
"""
SplashConfig(
enabled=True,
).save()
request = self.build_request(url_path='/heartbeat')
response = self.splash_middleware.process_request(request)
self.assertEquals(response, None)
def test_unaffected_path(self):
"""
Unaffected paths should never be redirected - custom value
"""
SplashConfig(
enabled=True,
unaffected_url_paths='/test1,/my/url/',
).save()
request = self.build_request(url_path='/my/url/')
response = self.splash_middleware.process_request(request)
self.assertEquals(response, None)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment