summaryrefslogtreecommitdiff
path: root/coip/middleware.py
blob: ce5e1e892208b6175cd37d7b2314bd8b4c9c0f17 (plain)
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
'''
Created on Dec 13, 2010

@author: leifj
'''
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
from coip.apps.user.models import Identifier
from django_extensions.utils import uuid            
from django.contrib import auth
from django.contrib.auth.models import UNUSABLE_PASSWORD

def _headers(request,attr):
    v = request.META.get(attr)
    if not v:
        return None
    values = filter(lambda x: x != "(null)",v.split(";"))
    return values;

def meta1(request,attr):
    v = _headers(request,attr)
    if v:
        return v[0]
    else:
        return None
    
class COIPRemoteUserMiddleware(object):
    """
    Middleware for utilizing Web-server-provided authentication.

    If request.user is not authenticated, then this middleware attempts to
    authenticate the username passed in the ``REMOTE_USER`` request header.
    If authentication is successful, the user is automatically logged in to
    persist the user in the session.

    The header used is configurable and defaults to ``REMOTE_USER``.  Subclass
    this class and change the ``header`` attribute if you need to use a
    different header.
    """

    # Name of request header to grab username from.  This will be the key as
    # used in the request.META dictionary, i.e. the normalization of headers to
    # all uppercase and the addition of "HTTP_" prefix apply.
    header = "REMOTE_USER"

    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.")

        if request.user.is_authenticated():
            # this is to make internal users work too...
            if not request.user.is_anonymous():
                user = request.user
                identifier,created = Identifier.objects.get_or_create(user=user,value=user.username,type=Identifier.INTERNAL,verified=True)
                user.get_profile().identifier = identifier
            return
        
        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
        
        idp = meta1(request,'Shib-Identity-Provider')
        if not idp:
            raise Exception("No IdP information in request")
        
        user = None
        identifier = None
        try:
            # Try to find a user based on the identifier received. If we find it we turn
            # around and authenticate the username as if it was received in the header.
            idp = meta1(request,'Shib-Identity-Provider')
            identifier = Identifier.objects.get(value=username,type=Identifier.FEDERATION,idp=idp,verified=True)
            user = auth.authenticate(remote_user=identifier.user.username)
        except ObjectDoesNotExist:
            pass
        
        if user == None:
            # We've never seen this identifier before. Create a new random uuid for the
            # django username and associate the identifier with it.
            user = auth.authenticate(remote_user=uuid.uuid4());
            user.password = UNUSABLE_PASSWORD
            user.save()
            identifier = Identifier.objects.create(user=user,value=username,type=Identifier.FEDERATION,idp=idp,verified=True)
            
        if not identifier:
            raise Exception("Unable to create user/id mapping")
            
        update = False
        cn = meta1(request,'cn')
        fn = meta1(request,'givenName')
        ln = meta1(request,'sn')
        
        if not cn:
            cn = meta1(request,'displayName')
        if not cn and fn and ln:    
            cn = "%s %s" % (fn,ln)    
        if not cn:
            cn = "%s according to %s" % (identifier.value,identifier.idp)
        
        if identifier.display_name != cn:
            identifier.display_name = cn
            identifier.save()    
        
        mail = meta1(request,'mail')
        if mail:
            mail_id,created = Identifier.objects.get_or_create(user=user,value=mail,type=Identifier.EMAIL,verified=True)
            user.email = mail
            update = True
            
        if fn:
            identifier.user.first_name = fn
            update = True
        if ln:
            identifier.user.last_name = ln
            update = True
            
        if update:
            identifier.user.save()
            
        if user:
            # User is valid.  Set request.user and persist user in the session
            # by logging the user in.
            user.get_profile().identifier = identifier
            auth.login(request, user)
            

    def clean_username(self, username, request):
        """
        Allows the backend to clean the username, if the backend defines a
        clean_username method.
        """
        backend_str = request.session[auth.BACKEND_SESSION_KEY]
        backend = auth.load_backend(backend_str)
        try:
            username = backend.clean_username(username)
        except AttributeError: # Backend has no clean_username method.
            pass
        return username