Permalink
Browse files

Add authorization backends

  • Loading branch information...
1 parent 4e1c50a commit 162cd84dcedc1ca230f956e8680135de7859b113 @emesik committed Feb 21, 2013
Showing with 152 additions and 45 deletions.
  1. +7 −10 djiki/auth/base.py
  2. +94 −19 djiki/tests.py
  3. +1 −1 djiki/urls.py
  4. +16 −4 djiki/utils.py
  5. +34 −11 djiki/views.py
View
@@ -27,12 +27,6 @@ def can_edit(self, request, target):
def can_view_history(self, request, target):
return True
- def can_undo_revision(self, request, target, revision):
- return True
-
- def can_revert_to(self, request, target, revision):
- return True
-
class OnlyAuthenticatedEdits(UnrestrictedAccess):
"Only authenticated users can modify the contents."
@@ -42,8 +36,11 @@ def can_create(self, request, target):
def can_edit(self, request, target):
return request.user.is_authenticated()
- def can_undo_revision(self, request, target):
- return request.user.is_authenticated()
- def can_revert_to(self, request, target):
- return request.user.is_authenticated()
+class OnlyAdminEdits(UnrestrictedAccess):
+ "Only admin users can modify the contents."
+ def can_create(self, request, target):
+ return request.user.is_authenticated() and request.user.is_superuser
+
+ def can_edit(self, request, target):
+ return request.user.is_authenticated() and request.user.is_superuser
View
@@ -5,6 +5,7 @@
from django.test import TestCase
from django.test.client import Client
from . import models
+from .auth.base import UnrestrictedAccess
content1 = u"""
= Hello world! =
@@ -34,11 +35,20 @@
class SimpleTest(TestCase):
def setUp(self):
settings.DJIKI_SPACES_AS_UNDERSCORES = False
- settings.DJIKI_ALLOW_ANONYMOUS_EDITS = True
- self.user1 = User.objects.create(username='foouser')
- self.password1 = 'foopassword'
- self.user1.set_password(self.password1)
- self.user1.save()
+ settings.DJIKI_AUTHORIZATION_BACKEND = 'djiki.auth.base.UnrestrictedAccess'
+ self.user = User.objects.create(username='foouser')
+ user_password = 'foopassword'
+ self.user.set_password(user_password)
+ self.user.save()
+ self.admin = User.objects.create(username='admin', is_superuser=True)
+ admin_password = 'adminpassword'
+ self.admin.set_password(admin_password)
+ self.admin.save()
+ self.anon_client = Client()
+ self.user_client = Client()
+ self.admin_client = Client()
+ self.user_client.login(username='foouser', password=user_password)
+ self.admin_client.login(username='admin', password=admin_password)
def _page_edit(self, title, content, description='', username=None, password=None):
client = Client()
@@ -63,10 +73,6 @@ def _page_edit(self, title, content, description='', username=None, password=Non
self.assertEqual(p.last_revision().author, None)
self.assertEqual(p.last_revision().description, description)
- def test_new_page_creation(self):
- self._page_edit(u"User Page", content1, description1, self.user1.username, self.password1)
- self._page_edit(u"Anonymous Page", content2, description2)
-
def test_subsequent_edits(self):
title = u"Test page"
self._page_edit(title, content1, description1)
@@ -102,23 +108,92 @@ def test_edit_crash(self):
print r.content
self.assertEqual(r.status_code, 302)
- def test_anonymous_edits(self):
+ def test_edits(self):
title = u"Auth test page"
- anon_client = Client()
- user_client = Client()
- user_client.login(username='foouser', password='foopassword')
- settings.DJIKI_ALLOW_ANONYMOUS_EDITS = False
- r = anon_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ settings.DJIKI_AUTHORIZATION_BACKEND = 'djiki.auth.base.UnrestrictedAccess'
+ # anonymous create
+ r = self.anon_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ {'content': "blah", "description": ""})
+ self.assertEqual(r.status_code, 302)
+ # anonymous edit
+ last_pk = models.Page.objects.get(title=title).last_revision().pk
+ r = self.anon_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ {'content': "blah", "description": "", 'prev_revision': last_pk})
+ self.assertEqual(r.status_code, 302)
+ # authenticated edit
+ last_pk = models.Page.objects.get(title=title).last_revision().pk
+ r = self.user_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ {'content': "blah", "description": "", 'prev_revision': last_pk})
+ self.assertEqual(r.status_code, 302)
+ # admin edit
+ last_pk = models.Page.objects.get(title=title).last_revision().pk
+ r = self.admin_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ {'content': "blah", "description": "", 'prev_revision': last_pk})
+ self.assertEqual(r.status_code, 302)
+
+ settings.DJIKI_AUTHORIZATION_BACKEND = 'djiki.auth.base.OnlyAuthenticatedEdits'
+ # anonymous create
+ r = self.anon_client.post(reverse('djiki-page-edit', kwargs={'title': u'Other title 1'}),
{'content': "blah", "description": ""})
self.assertEqual(r.status_code, 403)
- r = user_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ # anonymous edit
+ last_pk = models.Page.objects.get(title=title).last_revision().pk
+ r = self.anon_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ {'content': "blah", "description": "", 'prev_revision': last_pk})
+ self.assertEqual(r.status_code, 403)
+ # authenticated create
+ r = self.user_client.post(reverse('djiki-page-edit', kwargs={'title': u'Other title 2'}),
{'content': "blah", "description": ""})
self.assertEqual(r.status_code, 302)
+ # authenticated edit
+ r = self.user_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ {'content': "blah", "description": "", 'prev_revision': last_pk})
+ self.assertEqual(r.status_code, 302)
+ # admin edit
last_pk = models.Page.objects.get(title=title).last_revision().pk
- settings.DJIKI_ALLOW_ANONYMOUS_EDITS = True
- r = anon_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ r = self.admin_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
{'content': "blah", "description": "", 'prev_revision': last_pk})
self.assertEqual(r.status_code, 302)
- r = user_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+
+ settings.DJIKI_AUTHORIZATION_BACKEND = 'djiki.auth.base.OnlyAdminEdits'
+ # anonymous create
+ r = self.anon_client.post(reverse('djiki-page-edit', kwargs={'title': u'Other title 3'}),
+ {'content': "blah", "description": ""})
+ self.assertEqual(r.status_code, 403)
+ # anonymous edit
+ last_pk = models.Page.objects.get(title=title).last_revision().pk
+ r = self.anon_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ {'content': "blah", "description": "", 'prev_revision': last_pk})
+ self.assertEqual(r.status_code, 403)
+ # authenticated create
+ r = self.user_client.post(reverse('djiki-page-edit', kwargs={'title': u'Other title 4'}),
+ {'content': "blah", "description": ""})
+ self.assertEqual(r.status_code, 403)
+ # authenticated edit
+ r = self.user_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
+ {'content': "blah", "description": "", 'prev_revision': last_pk})
+ self.assertEqual(r.status_code, 403)
+ # admin create
+ r = self.admin_client.post(reverse('djiki-page-edit', kwargs={'title': u'Other title 5'}),
+ {'content': "blah", "description": ""})
+ self.assertEqual(r.status_code, 302)
+ # admin edit
+ r = self.admin_client.post(reverse('djiki-page-edit', kwargs={'title': title}),
{'content': "blah", "description": "", 'prev_revision': last_pk})
self.assertEqual(r.status_code, 302)
+
+ def test_history_view(self):
+ title = u"History page"
+ self._page_edit(title, "foo bar", "baz")
+
+ settings.DJIKI_AUTHORIZATION_BACKEND = 'djiki.auth.base.UnrestrictedAccess'
+ r = self.anon_client.get(reverse('djiki-page-history', kwargs={'title': title}))
+ self.assertEqual(r.status_code, 200)
+
+ class NoHistoryView(UnrestrictedAccess):
+ def can_view_history(self, request, target):
+ return False
+
+ settings.DJIKI_AUTHORIZATION_BACKEND = NoHistoryView
+ r = self.anon_client.get(reverse('djiki-page-history', kwargs={'title': title}))
+ self.assertEqual(r.status_code, 403)
View
@@ -1,4 +1,4 @@
-from django.conf.urls.defaults import *
+from django.conf.urls import url, patterns
from . import views
urlpatterns = patterns('',
View
@@ -1,19 +1,19 @@
import re
+import warnings
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.importlib import import_module
-def get_parser():
- setting = getattr(settings, 'DJIKI_PARSER', 'djiki.parsers.wikicreole')
+def _setting_to_instance(setting):
if isinstance(setting, basestring):
try:
parser = import_module(setting)
except ImportError:
try:
mpath, cname = setting.rsplit('.', 1)
except ValueError:
- raise ImproperlyConfigured('Invalid DJIKI_PARSER: "%s" does not '
- 'describe a module, object or class' % setting)
+ raise ImproperlyConfigured('"%s" does not describe a module, '
+ 'object or class' % setting)
module = import_module(mpath)
klass = getattr(module, cname)
parser = klass()
@@ -22,6 +22,18 @@ def get_parser():
return setting()
return setting
+def get_parser():
+ setting = getattr(settings, 'DJIKI_PARSER', 'djiki.parsers.wikicreole')
+ return _setting_to_instance(setting)
+
+def get_auth_backend():
+ if hasattr(settings, 'DJIKI_ALLOW_ANONYMOUS_EDITS'):
+ warnings.warn('The DJIKI_ALLOW_ANONYMOUS_EDITS is no longer used. '
+ 'Set DJIKI_AUTHORIZATION_BACKEND instead', DeprecationWarning)
+ setting = getattr(settings, 'DJIKI_AUTHORIZATION_BACKEND',
+ 'djiki.auth.base.UnrestrictedAccess')
+ return _setting_to_instance(setting)
+
def spaces_as_underscores():
return getattr(settings, 'DJIKI_SPACES_AS_UNDERSCORES', True)
View
@@ -11,10 +11,6 @@
from urllib import urlencode, quote
from . import models, forms, utils
-
-def allow_anonymous_edits():
- return getattr(settings, 'DJIKI_ALLOW_ANONYMOUS_EDITS', True)
-
def view(request, title, revision_pk=None):
url_title = utils.urlize_title(title)
if title != url_title:
@@ -23,13 +19,18 @@ def view(request, title, revision_pk=None):
kwargs={'title': url_title, 'revision_pk': revision_pk}))
return HttpResponseRedirect(reverse('djiki-page-view', kwargs={'title': url_title}))
page_title = utils.deurlize_title(title)
+ auth = utils.get_auth_backend()
try:
page = models.Page.objects.get(title=page_title)
except models.Page.DoesNotExist:
t = loader.get_template('djiki/not_found.html')
c = RequestContext(request, {'title': page_title})
return HttpResponseNotFound(t.render(c))
+ if not auth.can_view(request, page):
+ return HttpResponseForbidden()
if revision_pk:
+ if not auth.can_view_history(request, page):
+ return HttpResponseForbidden()
try:
revision = page.revisions.get(pk=revision_pk)
except models.PageRevision.DoesNotExist:
@@ -50,18 +51,21 @@ def view(request, title, revision_pk=None):
{'page': page, 'revision': revision})
def edit(request, title):
- if not allow_anonymous_edits() and not request.user.is_authenticated():
- return HttpResponseForbidden()
url_title = utils.urlize_title(title)
if title != url_title:
return HttpResponseRedirect(reverse('djiki-page-edit', kwargs={'title': url_title}))
page_title = utils.deurlize_title(title)
+ auth = utils.get_auth_backend()
try:
page = models.Page.objects.get(title=page_title)
last_content = page.last_revision().content
+ if not auth.can_edit(request, page):
+ return HttpResponseForbidden()
except models.Page.DoesNotExist:
page = models.Page(title=page_title)
last_content = ''
+ if not auth.can_create(request, page):
+ return HttpResponseForbidden()
revision = models.PageRevision(page=page,
author=request.user if request.user.is_authenticated() else None)
form = forms.PageEditForm(
@@ -90,15 +94,22 @@ def history(request, title):
return HttpResponseRedirect(reverse('djiki-page-history', kwargs={'title': url_title}))
page_title = utils.deurlize_title(title)
page = get_object_or_404(models.Page, title=page_title)
+ auth = utils.get_auth_backend()
+ if not auth.can_view_history(request, page):
+ return HttpResponseForbidden()
history = page.revisions.order_by('-created')
return TemplateResponse(request, 'djiki/history.html', {'page': page, 'history': history})
+
def diff(request, title):
url_title = utils.urlize_title(title)
if title != url_title:
return HttpResponseNotFound()
page_title = utils.deurlize_title(title)
page = get_object_or_404(models.Page, title=page_title)
+ auth = utils.get_auth_backend()
+ if not auth.can_view_history(request, page):
+ return HttpResponseForbidden()
try:
from_rev = page.revisions.get(pk=request.REQUEST['from_revision_pk'])
to_rev = page.revisions.get(pk=request.REQUEST['to_revision_pk'])
@@ -118,6 +129,9 @@ def revert(request, title, revision_pk):
reverse('djiki-page-revert', kwargs={'title': url_title, 'revision_pk': revision_pk}))
page_title = utils.deurlize_title(title)
page = get_object_or_404(models.Page, title=page_title)
+ auth = utils.get_auth_backend()
+ if not auth.can_edit(request, page):
+ return HttpResponseForbidden()
src_revision = get_object_or_404(models.PageRevision, page=page, pk=revision_pk)
new_revision = models.PageRevision(page=page,
author=request.user if request.user.is_authenticated() else None)
@@ -139,14 +153,15 @@ def revert(request, title, revision_pk):
{'page': page, 'form': form, 'src_revision': src_revision})
def undo(request, title, revision_pk):
- if not allow_anonymous_edits() and not request.user.is_authenticated():
- return HttpResponseForbidden()
url_title = utils.urlize_title(title)
if title != url_title:
return HttpResponseRedirect(
reverse('djiki-page-undo', kwargs={'title': url_title, 'revision_pk': revision_pk}))
page_title = utils.deurlize_title(title)
page = get_object_or_404(models.Page, title=page_title)
+ auth = utils.get_auth_backend()
+ if not auth.can_edit(request, page):
+ return HttpResponseForbidden()
src_revision = get_object_or_404(models.PageRevision, page=page, pk=revision_pk)
new_revision = models.PageRevision(page=page,
author=request.user if request.user.is_authenticated() else None)
@@ -188,7 +203,8 @@ def undo(request, title, revision_pk):
return TemplateResponse(request, 'djiki/edit.html', {'page': page, 'form': form})
def image_new(request):
- if not allow_anonymous_edits() and not request.user.is_authenticated():
+ auth = utils.get_auth_backend()
+ if not auth.can_create(request, models.Image()):
return HttpResponseForbidden()
form = forms.NewImageUploadForm(data=request.POST or None, files=request.FILES or None)
if request.method == 'POST':
@@ -204,16 +220,20 @@ def image_view(request, name):
return HttpResponseRedirect(reverse('djiki-image-view', kwargs={'name': url_name}))
image_name = utils.deurlize_title(name)
image = get_object_or_404(models.Image, name=image_name)
+ auth = utils.get_auth_backend()
+ if not auth.can_view(request, image):
+ return HttpResponseForbidden()
return TemplateResponse(request, 'djiki/image_view.html', {'image': image})
def image_edit(request, name):
- if not allow_anonymous_edits() and not request.user.is_authenticated():
- return HttpResponseForbidden()
url_name = utils.urlize_title(name)
if name != url_name:
return HttpResponseRedirect(reverse('djiki-image-edit', kwargs={'name': url_name}))
image_name = utils.deurlize_title(name)
image = get_object_or_404(models.Image, name=image_name)
+ auth = utils.get_auth_backend()
+ if not auth.can_edit(request, image):
+ return HttpResponseForbidden()
revision = models.ImageRevision(image=image,
author=request.user if request.user.is_authenticated() else None)
form = forms.ImageUploadForm(data=request.POST or None, files=request.FILES or None,
@@ -231,5 +251,8 @@ def image_history(request, name):
return HttpResponseRedirect(reverse('djiki-image-view', kwargs={'name': url_name}))
image_name = utils.deurlize_title(name)
image = get_object_or_404(models.Image, name=image_name)
+ auth = utils.get_auth_backend()
+ if not auth.can_view_history(request, image):
+ return HttpResponseForbidden()
history = image.revisions.order_by('-created')
return TemplateResponse(request, 'djiki/image_history.html', {'image': image, 'history': history})

0 comments on commit 162cd84

Please sign in to comment.