--- /dev/null
+"""Django CAS 1.0/2.0 authentication backend"""
+
+from django.conf import settings
+
+__all__ = []
+
+_DEFAULTS = {
+ 'CAS_ADMIN_PREFIX': None,
+ 'CAS_EXTRA_LOGIN_PARAMS': None,
+ 'CAS_IGNORE_REFERER': False,
+ 'CAS_LOGOUT_COMPLETELY': True,
+ 'CAS_REDIRECT_URL': '/',
+ 'CAS_RETRY_LOGIN': False,
+ 'CAS_SERVER_URL': None,
+ 'CAS_VERSION': '2',
+}
+
+for key, value in _DEFAULTS.iteritems():
+ try:
+ getattr(settings, key)
+ except AttributeError:
+ setattr(settings, key, value)
+ # Suppress errors from DJANGO_SETTINGS_MODULE not being set
+ except ImportError:
+ pass
--- /dev/null
+"""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 lxml import etree as ElementTree
+ except ImportError:
+ from elementtree import ElementTree
+
+ params = {'ticket': ticket, 'service': service}
+ url = (urljoin(settings.CAS_SERVER_URL, 'serviceValidate') + '?' +
+ 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
+ except:
+ import traceback
+ traceback.print_exc()
+ print "****"
+ print response
+ print "****"
+ 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__iexact = 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
--- /dev/null
+"""Replacement authentication decorators that work around redirection loops"""
+
+try:
+ from functools import wraps
+except ImportError:
+ from django.utils.functional import wraps
+
+from django.contrib.auth import REDIRECT_FIELD_NAME
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponseForbidden, HttpResponseRedirect
+from django.utils.http import urlquote
+
+__all__ = ['login_required', 'permission_required', 'user_passes_test']
+
+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.
+ """
+
+ if not login_url:
+ from django.conf import settings
+ login_url = settings.LOGIN_URL
+
+ def decorator(view_func):
+ @wraps(view_func)
+ def wrapper(request, *args, **kwargs):
+ if test_func(request.user):
+ return view_func(request, *args, **kwargs)
+ elif request.user.is_authenticated():
+ return HttpResponseForbidden('<h1>Permission denied</h1>')
+ else:
+ path = '%s?%s=%s' % (login_url, redirect_field_name,
+ urlquote(request.get_full_path()))
+ return HttpResponseRedirect(path)
+ return wrapper
+ return decorator
+
+
+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)
--- /dev/null
+"""CAS authentication middleware"""
+
+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):
+ """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)
+
+ if settings.CAS_ADMIN_PREFIX:
+ if not request.path.startswith(settings.CAS_ADMIN_PREFIX):
+ return None
+ elif not view_func.__module__.startswith('django.contrib.admin.'):
+ 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>')
+ return HttpResponseForbidden(error)
+ params = urlencode({REDIRECT_FIELD_NAME: request.get_full_path()})
+ return HttpResponseRedirect(reverse(cas_login) + '?' + params)
--- /dev/null
+from django.db import models
+from django.contrib.auth.models import User
\ No newline at end of file
--- /dev/null
+"""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.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.encode('utf-8')})
+ 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}
+ if settings.CAS_EXTRA_LOGIN_PARAMS:
+ params.update(settings.CAS_EXTRA_LOGIN_PARAMS)
+ return urljoin(settings.CAS_SERVER_URL, 'login') + '?' + urlencode(params)
+
+
+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, required = False):
+ """Forwards to CAS login URL or verifies CAS ticket"""
+
+ print "LOGIN original NEXT_PAGE:", next_page
+ print request.GET
+ if not next_page:
+ next_page = _redirect_url(request)
+ print "LOGIN redirect NEXT_PAGE:", next_page
+
+ 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)
+ print "TICKET", ticket
+ print "SERVICE", service
+ if ticket:
+ from django.contrib import auth
+ user = auth.authenticate(ticket = ticket, service = service)
+ if user is not None:
+ auth.login(request, user)
+ name = user.first_name or user.username
+ message = "Login succeeded. Welcome, %s." % name
+ user.message_set.create(message = message)
+ return HttpResponseRedirect(next_page)
+ elif settings.CAS_RETRY_LOGIN or required:
+ return HttpResponseRedirect(_login_url(service))
+ else:
+ error = "<h1>Forbidden</h1><p>Login failed.</p>"
+ return HttpResponseForbidden(error)
+
+ else:
+ return HttpResponseRedirect(_login_url(service))
+
+
+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)
+ if settings.CAS_LOGOUT_COMPLETELY:
+ return HttpResponseRedirect(_logout_url(request, next_page))
+ else:
+ return HttpResponseRedirect(next_page)
def document_gallery(request, directory):
try:
base_dir = os.path.join(settings.MEDIA_ROOT, settings.FILEBROWSER_DIRECTORY, directory)
- images = ['%s%s%s/%s' % (settings.MEDIA_URL, settings.FILEBROWSER_DIRECTORY, directory, f) for f in os.listdir(base_dir) if os.path.splitext(f)[1].lower() in ('.jpg', '.jpeg', '.png')]
+ images = [u'%s%s%s/%s' % (settings.MEDIA_URL, settings.FILEBROWSER_DIRECTORY, directory, f) for f in os.listdir(base_dir) if os.path.splitext(f)[1].lower() in (u'.jpg', u'.jpeg', u'.png')]
images.sort()
return HttpResponse(json.dumps(images))
except (IndexError, OSError), e:
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django_cas.middleware.CASMiddleware',
'django.middleware.doc.XViewMiddleware',
'maintenancemode.middleware.MaintenanceModeMiddleware',
)
+AUTHENTICATION_BACKENDS = (
+ 'django.contrib.auth.backends.ModelBackend',
+ 'django_cas.backends.CASBackend',
+)
+
ROOT_URLCONF = 'urls'
TEMPLATE_DIRS = (
PROJECT_ROOT + '/templates',
)
+
+#
+# Central Auth System
+#
+## Set this to where the CAS server lives
+# CAS_SERVER_URL = "http://cas.fnp.pl/
+CAS_ADMIN_PREFIX = "/admin/"
+CAS_LOGOUT_COMPLETELY = True
+
# CSS and JS files to compress
# COMPRESS_CSS = {
# 'all': {
'toolbar',
)
+
+#
+# Nose tests
+#
+
TEST_RUNNER = 'django_nose.run_tests'
TEST_MODULES = ('wiki', 'toolbar', 'vstorage')
NOSE_ARGS = (
ch.setFormatter(formatter)
log.addHandler(ch)
-
# Import localsettings file, which may override settings defined here
try:
- EXTRA_INSTALLED_APPS = tuple()
from localsettings import *
- INSTALLED_APPS += EXTRA_INSTALLED_APPS
except ImportError:
pass
{% if user.is_authenticated %}
<span class="user_name">{{ user.username }}</span> |
-<a href='{% url logout %}?next={{request.get_full_path|urlencode}}'>{% trans "Log Out" %}</a>
+<a href='{% url logout %}'>{% trans "Log Out" %}</a>
{% else %}
{% url login as login_url %}
{% ifnotequal login_url request.path %}
- <a href='{{ login_url }}?next={{request.get_full_path|urlencode}}'>{% trans "Log In" %}</a>
+ <a href='{{ login_url }}'>{% trans "Log In" %}</a>
{% endifnotequal %}
{% endif %}
</div>
<div id="header">
<div id="tools" style="float: right;">
- {% if user.is_authenticated %}
- Zalogowany jako <span id="username">{{ user }}</span> | <a href="{% url logout %}">Wyloguj</a> |
- {% else %}
- <a href="{% url login %}?next={{ request.path }}">Zaloguj się</a> |
- {% endif %}
+ {% include "registration/head_login.html" %}
Wersja: <span id="document-revision">{{ document.revision }}</span> <button style="margin-left: 6px" id="save-button">Zapisz</button></div>
<h1><a href="{% url wiki.views.document_list %}">Platforma</a></h1>
<ol id="tabs">
admin.autodiscover()
-if 'cas_consumer' in settings.INSTALLED_APPS:
- auth_views = 'cas_consumer.views'
-else:
- auth_views = 'django.contrib.auth.views'
-
urlpatterns = patterns('',
url(r'^$', 'wiki.views.document_list'),
url(r'^gallery/(?P<directory>[^/]+)$', 'wiki.views.document_gallery'),
# Auth
- url(r'^accounts/login/$', auth_views + '.login', name = 'login'),
- url(r'^accounts/logout/$', auth_views + '.logout', name = 'logout'),
+ url(r'^accounts/login/$', 'django_cas.views.login', name = 'login'),
+ url(r'^accounts/logout/$', 'django_cas.views.logout', name = 'logout'),
# Admin panel
(r'^admin/filebrowser/', include('filebrowser.urls')),