Commit a8b59add by Brodie Rao

Merged 2.0 branch into trunk

parent 2fac1a25
include Makefile *.txt
PREFIX=/usr/local
PYTHON=python
all: build
build:
$(PYTHON) setup.py build
clean:
$(PYTHON) setup.py clean --all
find . -name '*.py[co]' -exec rm -f "{}" ';'
rm -rf build dist django_cas.egg-info temp
install:
$(PYTHON) setup.py install --prefix="$(PREFIX)"
Django CAS:
Original Author: Brian Beck <exogen@gmail.com>
Current Maintainer/User: Chris Green <greencm@gmail.com>
Django CAS authentication module for a while, I decided to make a couple improvements.
The biggest improvement is that instead of modifying code in the CAS
module itself to set your CAS address and do things like custom User
field population, all this stuff can now be configured in your
settings file.
Another improvement is that CAS authentication now works for the
bundled admin interface. Since the administration interface does not
account for an authentication backend that doesn't know the user's
password, this makes the login form useless. The CAS module will now
intercept requests to the administration interface and do the proper
authentication routine if necessary, never showing the login form
(which doesn't make sense for CAS). Intercepting requests, you ask?
Yes, that means the CAS module is now middleware. Actually it's
middleware, a couple views, and an authentication backend. This
authentication backend actually just augments the built-in
authentication backend so both must be enabled even though credentials
from the built-in contrib.auth backend will fail!
Place the django_cas directory in your PYTHONPATH.
Now add it to the middleware and authentication backends in your settings. Make sure you also have the authentication middleware installed. Here's what mine looks like:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django_cas.cas.middleware.CASMiddleware',
'django.middleware.doc.XViewMiddleware',
)
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'django.contrib.cas.backend.CASBackend',
)
You can now configure the CAS module in the same settings file. Here are the possible options, most of which can be safely ignored:
* CAS_SERVER_URL: This is the only setting you must explicitly define. Set it to the base URL of your CAS source.
* CAS_POPULATE_USER: A callable or the location of a callable. When a user logs in and is missing name and email attributes in the database, this will be called with their User model instance. Default is None (do nothing).
* CAS_ADMIN_PREFIX: The URL prefix of the Django administration site. If undefined, the CAS middleware will just check the view being rendered to see if it lives in django.contrib.admin.views. The method is a little evil, but it works.
* CAS_LOGIN_URL: The URL where you bound django.contrib.cas.views.login. If undefined, assume /accounts/login/.
* CAS_LOGOUT_URL: The URL where you bound django.contrib.cas.views.logout. If undefined, assume /accounts/logout/.
* CAS_REDIRECT_URL: Where to send a user after logging in or out if there is no referrer and no next page set. Default is /.
* CAS_REDIRECT_FIELD_NAME: The name of the GET parameter in which to store the page URL to send the user to after logging in. Default is next.
Need an example? Here's what my CAS settings look like:
CAS_SERVER_URL = 'https://login.example.edu/cas/'
CAS_POPULATE_USER = 'present.utils.populate_user'
And the callable that lives at present.utils.populate_user (notice this code lives in my project instead of tinkering with the CAS module) looks like this:
def populate_user(user):
try:
ldap = LDAP()
person = ldap.filter_one_by(uid=user.username)
except:
if not user.email:
user.email = "%s@case.edu" % user.username
else:
# If it succeeds, update their User entry
user.email = person.mail[0]
user.first_name = fix_case(person.givenName[0])
user.last_name = fix_case(person.sn[0])
(LDAP and fix_case also live in my utils module).
Finally, make sure your project knows how to log users in and out by adding these to your URLconf:
(r'^accounts/login/$', 'django.contrib.cas.views.login'),
(r'^accounts/logout/$', 'django.contrib.cas.views.logout'),
Users should now be able to log into your site, and staff into the administration interface, using CAS 1.0.
If you require the use of the @permission_required decorator, there is
a known issue that can cause an infinite loop. You can look at
applying the django/contrib/auth/decorators.py to integrate the two.
There is also a permfail.py that worked with older 0.96 versions of
the django framework.
For more information, see
http://groups.google.com/group/django-developers/browse_thread/thread/8f81ed9644dfc84e
Django CAS
==========
`django_cas` is a CAS 1.0 and CAS 2.0 authentication backend for Django. It
allows you to use Django's built-in authentication mechanisms and `User`
model while adding support for CAS.
It also includes a middleware that intercepts calls to the original login
and logout pages and forwards them to the CASified versions, and adds
CAS support to the admin interface.
Installation
------------
Run `python setup.py install`, or place the `django_cas` directory in your
`PYTHONPATH` directly.
Now add it to the middleware and authentication backends in your settings.
Make sure you also have the authentication middleware installed. Here's what
mine looks like:
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django_cas.middleware.CASMiddleware',
'django.middleware.doc.XViewMiddleware',
)
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'django_cas.backends.CASBackend',
)
Set the following required setting in `settings.py`:
* `CAS_SERVER_URL`: This is the only setting you must explicitly define.
Set it to the base URL of your CAS source (e.g.
http://sso.some.edu/cas/).
Optional settings include:
* `CAS_ADMIN_PREFIX`: The URL prefix of the Django administration site.
If undefined, the CAS middleware will check the view being rendered to
see if it lives in `django.contrib.admin.views`.
* `CAS_IGNORE_REFERER`: If `True`, logging out of the application will
always send the user to the URL specified by `CAS_REDIRECT_URL`.
* `CAS_LOGOUT_COMPLETELY`: If `False`, logging out of the application
won't log the user out of CAS as well.
* `CAS_REDIRECT_URL`: Where to send a user after logging in or out if
there is no referrer and no next page set. Default is `/`.
* `CAS_VERSION`: The CAS protocol version to use. `'1'` and `'2'` are
supported, with `'2'` being the default.
Make sure your project knows how to log users in and out by adding these to
your URL mappings:
{{{
(r'^accounts/login/$', 'django_cas.views.login'),
(r'^accounts/logout/$', 'django_cas.views.logout'),
}}}
Users should now be able to log into your site (and staff into the
administration interface) using CAS.
Populating user data
--------------------
To add user data, subclass `CASBackend` and specify that as your
application's backend.
For example:
from django_cas.backend import CASBackend
class PopulatedCASBackend(CASBackend):
"""CAS authentication backend with user data populated from AD"""
def authenticate(self, ticket, service):
"""Authenticates CAS ticket and retrieves user data"""
user = super(PopulatedCASBackend, self).authenticate(
ticket, service)
# Connect to AD, modify user object, etc.
return user
Preventing Infinite Redirects
-----------------------------
Django's current implementation of its `permission_required` and
`user_passes_test` decorators (in `django.contrib.auth.decorators`) have a
known issue that can cause users to experience infinite redirects. The
decorators return the user to the login page, even if they're already logged
in, which causes a loop with SSO services like CAS.
`django_cas` provides fixed versions of these decorators in
`django_cas.decorators`. Usage is unchanged, and in the event that this issue
is fixed, the decorators should still work without issue.
For more information see http://code.djangoproject.com/ticket/4617.
Customizing the 403 Error Page
------------------------------
Django doesn't provide a simple way to customize 403 error pages, so you'll
have to make a response middleware that handles `HttpResponseForbidden`.
For example, in `views.py`:
from django.http import HttpResponseForbidden
from django.template import Context, loader
def forbidden(request, template_name='403.html'):
"""Default 403 handler"""
t = loader.get_template(template_name)
return HttpResponseForbidden(t.render(Context({})))
And in `middleware.py`:
from django.http import HttpResponseForbidden
from yourapp.views import forbidden
class Custom403Middleware(object):
"""Catches 403 responses and renders 403.html"""
def process_response(self, request, response):
if isinstance(response, HttpResponseForbidden):
return forbidden(request)
else:
return response
Now add `yourapp.middleware.Custom403Middleware` to your `MIDDLEWARE_CLASSES`
setting and create a template named `403.html`.
CAS 2.0 support
---------------
The CAS 2.0 protocol is supported in the same way that 1.0 is; no extensions
or new features from the CAS 2.0 specification are implemented. `elementtree`
is required to use this functionality. (`elementtree` is also included in
Python 2.5's standard library.)
Note: The CAS 3.x server uses the CAS 2.0 protocol. There is no CAS 3.0
protocol, though the CAS 3.x server does allow extensions to the protocol.
Differences Between Django CAS 1.0 and 2.0
------------------------------------------
Version 2.0 of `django_cas` breaks compatibility in some small ways, in order
simplify the library. The following settings have been removed:
* `CAS_LOGIN_URL` and `CAS_LOGOUT_URL`: Version 2.0 is capable of
determining these automatically.
* `CAS_POPULATE_USER`: Subclass `CASBackend` instead (see above).
* `CAS_REDIRECT_FIELD_NAME`: Django's own `REDIRECT_FIELD_NAME` is now
used unconditionally.
from urllib import urlopen, urlencode
from urlparse import urljoin
"""Django CAS 1.0/2.0 authentication backend"""
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
defaults = {'CAS_SERVER_URL': None,
'CAS_POPULATE_USER': None,
'CAS_ADMIN_PREFIX': None,
'CAS_LOGIN_URL': '/accounts/login/',
'CAS_LOGOUT_URL': '/accounts/logout/',
'CAS_REDIRECT_URL': '/',
'CAS_REDIRECT_FIELD_NAME': 'next',
}
__all__ = []
for key, value in defaults.iteritems():
_DEFAULTS = {
'CAS_ADMIN_PREFIX': None,
'CAS_IGNORE_REFERER': False,
'CAS_LOGOUT_COMPLETELY': True,
'CAS_REDIRECT_URL': '/',
'CAS_SERVER_URL': None,
'CAS_VERSION': '2',
}
for key, value in _DEFAULTS.iteritems():
try:
getattr(settings, key)
except AttributeError:
setattr(settings, key, value)
def get_populate_callback(callback):
if callable(callback):
return callback
try:
dot = callback.rindex('.')
except ValueError:
from django.core import exceptions
error = "Error importing CAS_POPULATE_USER callback: %s" % callback
raise exceptions.ImproperlyConfigured(error)
module_name, func_name = callback[:dot], callback[dot + 1:]
module = __import__(module_name, {}, {}, [''])
try:
func = getattr(module, func_name)
except AttributeError:
from django.core import exceptions
error = "Error importing CAS_POPULATE_USER callback: %s" % callback
raise exceptions.ImproperlyConfigured(error)
assert callable(func)
return func
def populate_user(user):
if settings.CAS_POPULATE_USER:
callback = get_populate_callback(settings.CAS_POPULATE_USER)
return callback(user)
def service_url(request, redirect_to=None):
from django.http import get_host
host = get_host(request)
protocol = request.is_secure() and 'https://' or 'http://'
service = protocol + host + request.path
if redirect_to:
if '?' in service:
service += '&'
else:
service += '?'
service += urlencode({settings.CAS_REDIRECT_FIELD_NAME: redirect_to})
return service
def redirect_url(request):
next = request.GET.get(settings.CAS_REDIRECT_FIELD_NAME)
if not next:
next = request.META.get('HTTP_REFERER', settings.CAS_REDIRECT_URL)
return next
def login_url(service):
params = {'service': service}
url = urljoin(settings.CAS_SERVER_URL, 'login') + '?' + urlencode(params)
if settings.DEBUG: print "Logging in: %s" % url
return url
def validate_url():
return urljoin(settings.CAS_SERVER_URL, 'validate')
def verify(ticket, service):
params = {'ticket': ticket, 'service': service}
url = validate_url() + '?' + urlencode(params)
if settings.DEBUG: print "Verifying ticket: %s" % url
page = urlopen(url)
verified = page.readline().strip()
username = page.readline().strip()
if verified == 'yes':
return username
return None
# Suppress errors from DJANGO_SETTINGS_MODULE not being set
except ImportError:
pass
from django_cas import verify, populate_user
from models import User
class CASBackend(object):
def authenticate(self, ticket, service):
username = verify(ticket, service)
if username:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# Password doesn't matter, it won't be used
password = User.objects.make_random_password()
user = User.objects.create_user(username, '', password)
populate_user(user)
user.save()
else:
# User has logged in before
if not (user.first_name and user.last_name and user.email):
populate_user(user)
user.save()
return user
return None
def get_user(self, user_id):
"""Retrieve the user's entry in the User model if it exists."""
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
"""CAS authentication backend"""
from urllib import urlencode, urlopen
from urlparse import urljoin
from django.conf import settings
from django_cas.models import User
__all__ = ['CASBackend']
def _verify_cas1(ticket, service):
"""Verifies CAS 1.0 authentication ticket.
Returns username on success and None on failure.
"""
params = {'ticket': ticket, 'service': service}
url = (urljoin(settings.CAS_SERVER_URL, 'validate') + '?' +
urlencode(params))
page = urlopen(url)
try:
verified = page.readline().strip()
if verified == 'yes':
return page.readline().strip()
else:
return None
finally:
page.close()
def _verify_cas2(ticket, service):
"""Verifies CAS 2.0+ XML-based authentication ticket.
Returns username on success and None on failure.
"""
try:
from xml.etree import ElementTree
except ImportError:
from elementtree import ElementTree
params = {'ticket': ticket, 'service': service}
url = (urljoin(settings.CAS_SERVER_URL, 'proxyValidate') + '?' +
urlencode(params))
page = urlopen(url)
try:
response = page.read()
tree = ElementTree.fromstring(response)
if tree[0].tag.endswith('authenticationSuccess'):
return tree[0][0].text
else:
return None
finally:
page.close()
_PROTOCOLS = {'1': _verify_cas1, '2': _verify_cas2}
if settings.CAS_VERSION not in _PROTOCOLS:
raise ValueError('Unsupported CAS_VERSION %r' % settings.CAS_VERSION)
_verify = _PROTOCOLS[settings.CAS_VERSION]
class CASBackend(object):
"""CAS authentication backend"""
def authenticate(self, ticket, service):
"""Verifies CAS ticket and gets or creates User object"""
username = _verify(ticket, service)
if not username:
return None
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# user will have an "unusable" password
user = User.objects.create_user(username, '')
user.save()
return user
def get_user(self, user_id):
"""Retrieve the user's entry in the User model if it exists"""
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
"""Replacement authentication decorators that work around redirection loops"""
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import _CheckLogin
from django.http import HttpResponseForbidden, HttpResponseRedirect
__all__ = ['permission_required', 'user_passes_test']
class CheckLoginOrForbid(_CheckLogin):
def __call__(self, request, *args, **kwargs):
response = super(CheckLoginOrForbid, self).__call__(request, *args,
**kwargs)
if (isinstance(response, HttpResponseRedirect) and
request.user.is_authenticated()):
return HttpResponseForbidden('<h1>Permission denied</h1>')
else:
return response
def user_passes_test(test_func, login_url=None,
redirect_field_name=REDIRECT_FIELD_NAME):
"""Replacement for django.contrib.auth.decorators.user_passes_test that
returns 403 Forbidden if the user is already logged in.
"""
def decorate(view_func):
return CheckLoginOrForbid(view_func, test_func, login_url,
redirect_field_name)
return decorate
def permission_required(perm, login_url=None):
"""Replacement for django.contrib.auth.decorators.permission_required that
returns 403 Forbidden if the user is already logged in.
"""
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
from os import path
from django.http import HttpResponseRedirect, HttpResponseForbidden, urlencode
"""CAS authentication middleware"""
import os
from urllib import urlencode
from django.http import HttpResponseRedirect, HttpResponseForbidden
from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.contrib.auth.views import login, logout
from django.core.urlresolvers import reverse
from django_cas.views import login as cas_login, logout as cas_logout
__all__ = ['CASMiddleware']
class CASMiddleware(object):
"""Middleware that allows CAS authentication on admin pages"""
def process_request(self, request):
error = """The Django CAS middleware requires authentication
middleware to be installed. Edit your MIDDLEWARE_CLASSES
setting to insert 'django.contrib.auth.middleware.
AuthenticationMiddleware'."""
"""Checks that the authentication middleware is installed"""
error = ("The Django CAS middleware requires authentication "
"middleware to be installed. Edit your MIDDLEWARE_CLASSES "
"setting to insert 'django.contrib.auth.middleware."
"AuthenticationMiddleware'.")
assert hasattr(request, 'user'), error
def process_view(self, request, view_func, view_args, view_kwargs):
"""Forwards unauthenticated requests to the admin page to the CAS
login URL, as well as calls to django.contrib.auth.views.login and
logout.
"""
if view_func == login:
return cas_login(request, *view_args, **view_kwargs)
elif view_func == logout:
return cas_logout(request, *view_args, **view_kwargs)
admin_prefix = settings.CAS_ADMIN_PREFIX
if admin_prefix:
if not request.path.startswith(admin_prefix):
......@@ -20,19 +45,21 @@ AuthenticationMiddleware'."""
try:
view_file = view_func.func_code.co_filename
except AttributeError:
# if we get a protected decororator that abstracts this away into something like _CheckLogin
# If we get a protected decorator that abstracts this away
# into something like _CheckLogin
view_file = view_func.view_func.func_code.co_filename
view_path = path.split(view_file)[0].split(path.sep)[-4:]
view_path = os.path.split(view_file)[0].split(os.path.sep)[-4:]
if view_path != admin_path:
return None
if request.user.is_authenticated():
if request.user.is_staff:
return None
else:
error = "<h1>Forbidden</h1><p>You do not have staff privileges.</p>"
error = ('<h1>Forbidden</h1><p>You do not have staff '
'privileges.</p>')
return HttpResponseForbidden(error)
field, url = settings.CAS_REDIRECT_FIELD_NAME, settings.CAS_LOGIN_URL
params = urlencode({field: request.get_full_path()})
params = urlencode({REDIRECT_FIELD_NAME: request.get_full_path()})
url = reverse(cas_login)
return HttpResponseRedirect(url + '?' + params)
from django.http import HttpResponseRedirect, HttpResponseForbidden, urlencode
"""CAS login/logout replacement views"""
from urllib import urlencode
from urlparse import urljoin
from django.http import get_host, HttpResponseRedirect, HttpResponseForbidden
from django.conf import settings
from django_cas import redirect_url, service_url, login_url
from django.contrib.auth import REDIRECT_FIELD_NAME
__all__ = ['login', 'logout']
def _service_url(request, redirect_to=None):
"""Generates application service URL for CAS"""
protocol = ('http://', 'https://')[request.is_secure()]
host = get_host(request)
service = protocol + host + request.path
if redirect_to:
if '?' in service:
service += '&'
else:
service += '?'
service += urlencode({REDIRECT_FIELD_NAME: redirect_to})
return service
def _redirect_url(request):
"""Redirects to referring page, or CAS_REDIRECT_URL if no referrer is
set.
"""
next = request.GET.get(REDIRECT_FIELD_NAME)
if not next:
if settings.CAS_IGNORE_REFERER:
next = settings.CAS_REDIRECT_URL
else:
next = request.META.get('HTTP_REFERER', settings.CAS_REDIRECT_URL)
prefix = (('http://', 'https://')[request.is_secure()] +
get_host(request))
if next.startswith(prefix):
next = next[len(prefix):]
return next
def _login_url(service):
"""Generates CAS login URL"""
params = {'service': service}
url = urljoin(settings.CAS_SERVER_URL, 'login') + '?' + urlencode(params)
return url
def _logout_url(request, next_page=None):
"""Generates CAS logout URL"""
url = urljoin(settings.CAS_SERVER_URL, 'logout')
if next_page:
protocol = ('http://', 'https://')[request.is_secure()]
host = get_host(request)
url += '?' + urlencode({'url': protocol + host + next_page})
return url
def login(request, next_page=None):
"""Forwards to CAS login URL or verifies CAS ticket"""
if not next_page:
next_page = redirect_url(request)
next_page = _redirect_url(request)
if request.user.is_authenticated():
message = "You are logged in as %s." % request.user.username
request.user.message_set.create(message=message)
return HttpResponseRedirect(next_page)
ticket = request.GET.get('ticket')
service = service_url(request, next_page)
service = _service_url(request, next_page)
if ticket:
from django.contrib.auth import authenticate, login
user = authenticate(ticket=ticket, service=service)
......@@ -19,19 +80,23 @@ def login(request, next_page=None):
name = user.first_name or user.username
message = "Login succeeded. Welcome, %s." % name
user.message_set.create(message=message)
if settings.DEBUG:
print "Please welcome %s to the system." % user
return HttpResponseRedirect(next_page)
else:
error = "<h1>Forbidden</h1><p>Login failed.</p>"
return HttpResponseForbidden(error)
else:
url = login_url(service)
url = _login_url(service)
return HttpResponseRedirect(url)
def logout(request, next_page=None):
"""Redirects to CAS logout page"""
from django.contrib.auth import logout
logout(request)
if not next_page:
next_page = redirect_url(request)
return HttpResponseRedirect(next_page)
next_page = _redirect_url(request)
if settings.CAS_LOGOUT_COMPLETELY:
return HttpResponseRedirect(_logout_url(request, next_page))
else:
return HttpResponseRedirect(next_page)
Index: /usr/local/src/django/django/contrib/auth/decorators.py
===================================================================
--- /usr/local/src/django/django/contrib/auth/decorators.py (revision 6941)
+++ /usr/local/src/django/django/contrib/auth/decorators.py (working copy)
@@ -1,5 +1,5 @@
from django.contrib.auth import REDIRECT_FIELD_NAME
-from django.http import HttpResponseRedirect
+from django.http import HttpResponseRedirect, HttpResponseForbidden
from django.utils.http import urlquote
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
@@ -30,6 +30,7 @@
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.
"""
+ # import pdb; pdb.set_trace()
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
class _CheckLogin(object):
@@ -58,8 +59,19 @@
return _CheckLogin(view_func, self.test_func, self.login_url, self.redirect_field_name)
def __call__(self, request, *args, **kwargs):
+ """ Execute the test_function for the end user,
+ otherwise, redirect them to an appropriate page """
+
if self.test_func(request.user):
return self.view_func(request, *args, **kwargs)
+
path = urlquote(request.get_full_path())
+
+ if request.user.is_authenticated():
+ # pushing the user back through the login_url only makes
+ # sense if they haven't already done that.
+ return HttpResponseForbidden("<h1>Access Forbidden: You do not have rights to %s</h1>" % path)
+
tup = self.login_url, self.redirect_field_name, path
+
return HttpResponseRedirect('%s?%s=%s' % tup)
# PERMFAIL Modifications
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect, HttpResponseForbidden
from urllib import quote
from django.conf import settings
FAIL_URL = "/oops"
def authenticated_user_passes_test(test_func, login_url=settings.LOGIN_URL, fail_url=FAIL_URL):
"""
Decorator for views that checks that the user passes the given test,
redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes.
The failure url is for when people need to pass a test but needs a
response other than being redirected to the login page.
The auth_required option is there in case your test_function performs
"""
def _dec(view_func):
def _checklogin(request, *args, **kwargs):
# import pdb; pdb.set_trace()
if not request.user.is_authenticated():
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME,
quote(request.get_full_path())))
if test_func(request.user):
return view_func(request, *args, **kwargs)
# We have failed, give us the error page
error = "<h1>Forbidden</h1><p>You do not have sufficient permissions<p>"
return HttpResponseForbidden(error)
_checklogin.__doc__ = view_func.__doc__
_checklogin.__dict__ = view_func.__dict__
return _checklogin
return _dec
def permission_required(perm, login_url=settings.LOGIN_URL, fail_url=FAIL_URL):
"""
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.
"""
return authenticated_user_passes_test(lambda u: u.has_perm(perm), login_url=login_url,fail_url=fail_url)
#!/usr/bin/env python
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
setup(
author='Brodie Rao',
author_email='brodie.rao@cpcc.edu',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'Framework :: Django',
'License :: OSI Approved :: MIT License',
'Natural Language :: English',
'Operating System :: OS Independent',
'Topic :: Internet :: WWW/HTTP',
],
description='CAS 1.0/2.0 authentication backend for Django',
keywords='django cas cas2 authentication middleware backend',
license='MIT',
name='django_cas',
packages=['django_cas'],
url='http://code.google.com/p/django-cas/',
version='2.0',
)
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