Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Mixin: AnonymousUserOnlyMixin #89

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -8,3 +8,4 @@
/htmlcov
/.tox
dist/
/venv
40 changes: 38 additions & 2 deletions braces/views.py
Expand Up @@ -8,7 +8,7 @@
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.core.serializers.json import DjangoJSONEncoder
from django.core.urlresolvers import resolve, reverse
from django.http import HttpResponse, HttpResponseBadRequest
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
from django.utils.encoding import force_text
Expand Down Expand Up @@ -78,6 +78,43 @@ def dispatch(self, request, *args, **kwargs):
request, *args, **kwargs)


class AnonymousRequiredMixin(object):
"""
View mixin which redirects to a specified URL if authenticated.
Can be useful if you wanted to prevent authenticated users from
accessing signup pages etc.

NOTE:
This should be the left-most mixin of a view.

Example Usage

class SomeView(AnonymousRequiredMixin, ListView):
...
# required
authenticated_redirect_url = "/accounts/profile/"
...
"""
authenticated_redirect_url = settings.LOGIN_REDIRECT_URL # LOGIN_REDIRECT_URL from project settings

def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
return HttpResponseRedirect(self.get_authenticated_redirect_url())
return super(AnonymousRequiredMixin, self).dispatch(request, *args, **kwargs)

def get_authenticated_redirect_url(self):
# Return the reversed authenticated redirect url.
if self.authenticated_redirect_url is None:
raise ImproperlyConfigured(
"%(cls)s is missing a authenticated_redirect_url "
"name to reverse and redirect to. Define "
"%(cls)s.authenticated_redirect_url or override "
"%(cls)s.get_authenticated_redirect_url()"
"." % {"cls": self.__class__.__name__})
return reverse(self.authenticated_redirect_url)



class CsrfExemptMixin(object):
"""
Exempts the view from CSRF requirements.
Expand Down Expand Up @@ -139,7 +176,6 @@ def dispatch(self, request, *args, **kwargs):
return super(PermissionRequiredMixin, self).dispatch(
request, *args, **kwargs)


class MultiplePermissionsRequiredMixin(AccessMixin):
"""
View mixin which allows you to specify two types of permission
Expand Down
37 changes: 34 additions & 3 deletions tests/test_access_mixins.py
Expand Up @@ -5,9 +5,10 @@
from .compat import force_text
from .factories import GroupFactory, UserFactory
from .helpers import TestViewHelper
from .views import (PermissionRequiredView, MultiplePermissionsRequiredView,
SuperuserRequiredView, StaffuserRequiredView,
LoginRequiredView, GroupRequiredView)
from .views import (PermissionRequiredView, AnonymousRequiredView,
MultiplePermissionsRequiredView, SuperuserRequiredView,
StaffuserRequiredView, LoginRequiredView,
GroupRequiredView)


class _TestAccessBasicsMixin(TestViewHelper):
Expand Down Expand Up @@ -126,6 +127,36 @@ def test_authenticated(self):
assert resp.status_code == 200
assert force_text(resp.content) == 'OK'


class TestAnonymousRequiredMixin(TestViewHelper, test.TestCase):
"""
Tests for AnonymousRequiredMixin.
"""
view_class = AnonymousRequiredView
view_url = '/unauthenticated_view/'

def test_anonymous(self):
resp = self.client.get(self.view_url)
# As an non-authenticated user, it should be possible to access
# the URL.
assert resp.status_code == 200
assert force_text(resp.content) == 'OK'

# Test with reverse_lazy
resp = self.dispatch_view(self.build_request(), login_url=reverse_lazy('unauthenticated_view'))
assert resp.status_code == 200
assert force_text(resp.content) == 'OK'

def test_authenticated(self):
user = UserFactory()
self.client.login(username=user.username, password='asdf1234')
resp = self.client.get(self.view_url)
# Check that the authenticated user has been successfully directed
# to the approparite view.
assert resp.status_code == 302
resp = self.client.get(self.view_url, follow = True)
self.assertRedirects(resp, "/authenticated_view/")


class TestPermissionRequiredMixin(_TestAccessBasicsMixin, test.TestCase):
"""
Expand Down
6 changes: 6 additions & 0 deletions tests/urls.py
Expand Up @@ -6,6 +6,12 @@
'',
# LoginRequiredMixin tests
url(r'^login_required/$', views.LoginRequiredView.as_view()),

# AnonymousRequiredView tests
url(r'^unauthenticated_view/$', views.AnonymousRequiredView.as_view(),
name='unauthenticated_view'),
url(r'^authenticated_view/$', views.AuthenticatedView.as_view(),
name='authenticated_view'),

# AjaxResponseMixin tests
url(r'^ajax_response/$', views.AjaxResponseView.as_view()),
Expand Down
15 changes: 15 additions & 0 deletions tests/views.py
Expand Up @@ -33,6 +33,21 @@ class LoginRequiredView(views.LoginRequiredMixin, OkView):
A view for testing LoginRequiredMixin.
"""

class AnonymousRequiredView(views.AnonymousRequiredMixin, OkView):
"""
A view for testing AnonymousRequiredMixin. Should accept unauthenticated
users and redirect authenticated users to the authenticated_redirect_url
set on the view.
"""
def get_authenticated_redirect_url(self):
return "/authenticated_view/"

class AuthenticatedView(views.LoginRequiredMixin, OkView):
"""
A view for testing AnonymousRequiredMixin. Should accept
authenticated users.
"""


class AjaxResponseView(views.AjaxResponseMixin, OkView):
"""
Expand Down