-
Notifications
You must be signed in to change notification settings - Fork 72
/
middleware.py
executable file
·131 lines (117 loc) · 5.33 KB
/
middleware.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
from django.contrib.auth.middleware import RemoteUserMiddleware
from django.contrib.auth.models import Group
from django.contrib import auth
from django.core.exceptions import ImproperlyConfigured
from shibboleth.app_settings import SHIB_ATTRIBUTE_MAP, GROUP_ATTRIBUTES, LOGOUT_SESSION_KEY
class ShibbolethRemoteUserMiddleware(RemoteUserMiddleware):
"""
Authentication Middleware for use with Shibboleth. Uses the recommended pattern
for remote authentication from: http://code.djangoproject.com/svn/django/tags/releases/1.3/django/contrib/auth/middleware.py
"""
def process_request(self, request):
# AuthenticationMiddleware is required so that request.user exists.
if not hasattr(request, 'user'):
raise ImproperlyConfigured(
"The Django remote user auth middleware requires the"
" authentication middleware to be installed. Edit your"
" MIDDLEWARE_CLASSES setting to insert"
" 'django.contrib.auth.middleware.AuthenticationMiddleware'"
" before the RemoteUserMiddleware class.")
# To support logout. If this variable is True, do not
# authenticate user and return now.
if request.session.get(LOGOUT_SESSION_KEY):
return
else:
# Delete the shib reauth session key if present.
request.session.pop(LOGOUT_SESSION_KEY, None)
# Locate the remote user header.
try:
username = request.META[self.header]
except KeyError:
# If specified header doesn't exist then return (leaving
# request.user set to AnonymousUser by the
# AuthenticationMiddleware).
return
# If the user is already authenticated and that user is the user we are
# getting passed in the headers, then the correct user is already
# persisted in the session and we don't need to continue.
if request.user.is_authenticated():
if request.user.username == self.clean_username(username, request):
return
# Make sure we have all required Shiboleth elements before proceeding.
shib_meta, error = self.parse_attributes(request)
# Add parsed attributes to the session.
request.session['shib'] = shib_meta
if error:
raise ShibbolethValidationError("All required Shibboleth elements"
" not found. %s" % shib_meta)
# We are seeing this user for the first time in this session, attempt
# to authenticate the user.
user = auth.authenticate(remote_user=username, shib_meta=shib_meta)
if user:
# User is valid. Set request.user and persist user in the session
# by logging the user in.
request.user = user
auth.login(request, user)
user.set_unusable_password()
user.save()
# Upgrade user groups if configured in the settings.py
# If activated, the user will be associated with those groups.
if GROUP_ATTRIBUTES:
self.update_user_groups(request, user)
# call make profile.
self.make_profile(user, shib_meta)
# setup session.
self.setup_session(request)
def make_profile(self, user, shib_meta):
"""
This is here as a stub to allow subclassing of ShibbolethRemoteUserMiddleware
to include a make_profile method that will create a Django user profile
from the Shib provided attributes. By default it does nothing.
"""
return
def setup_session(self, request):
"""
If you want to add custom code to setup user sessions, you
can extend this.
"""
return
def update_user_groups(self, request, user):
groups = self.parse_group_attributes(request)
# Remove the user from all groups that are not specified in the shibboleth metadata
for group in user.groups.all():
if group.name not in groups:
group.user_set.remove(user)
# Add the user to all groups in the shibboleth metadata
for g in groups:
group, created = Group.objects.get_or_create(name=g)
group.user_set.add(user)
@staticmethod
def parse_attributes(request):
"""
Parse the incoming Shibboleth attributes and convert them to the internal data structure.
From: https://github.com/russell/django-shibboleth/blob/master/django_shibboleth/utils.py
Pull the mapped attributes from the apache headers.
"""
shib_attrs = {}
error = False
meta = request.META
for header, attr in list(SHIB_ATTRIBUTE_MAP.items()):
required, name = attr
value = meta.get(header, None)
shib_attrs[name] = value
if not value or value == '':
if required:
error = True
return shib_attrs, error
@staticmethod
def parse_group_attributes(request):
"""
Parse the Shibboleth attributes for the GROUP_ATTRIBUTES and generate a list of them.
"""
groups = []
for attr in GROUP_ATTRIBUTES:
groups += filter(bool, request.META.get(attr, '').split(';'))
return groups
class ShibbolethValidationError(Exception):
pass