Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / im / views / target / shibboleth.py @ b08aadc0

History | View | Annotate | Download (7.8 kB)

1
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from django.conf import settings as global_settings
35
from django.utils.translation import ugettext as _
36
from django.contrib import messages
37
from django.views.decorators.http import require_http_methods
38
from django.http import HttpResponseRedirect
39

    
40
from astakos.im.util import login_url
41
from astakos.im.models import AstakosUser, AstakosUserAuthProvider, \
42
    PendingThirdPartyUser
43
from astakos.im import settings
44
from astakos.im.views.target import get_pending_key, \
45
    handle_third_party_signup, handle_third_party_login, \
46
    init_third_party_session
47
from astakos.im.views.decorators import cookie_fix, requires_auth_provider
48

    
49
import astakos.im.messages as astakos_messages
50
import logging
51

    
52
logger = logging.getLogger(__name__)
53

    
54

    
55
def migrate_eppn_to_remote_id(eppn, remote_id):
56
    """
57
    Retrieve active and pending accounts that are associated with shibboleth
58
    using EPPN as the third party unique identifier update them by storing
59
    REMOTE_USER value instead.
60
    """
61
    if eppn == remote_id:
62
        return
63

    
64
    try:
65
        provider = AstakosUserAuthProvider.objects.get(module='shibboleth',
66
                                                       identifier=eppn)
67
        msg = "Migrating user %r eppn (%s -> %s)"
68
        logger.info(msg, provider.user.log_display, eppn, remote_id)
69
        provider.identifier = remote_id
70
        provider.save()
71
    except AstakosUserAuthProvider.DoesNotExist:
72
        pass
73

    
74
    pending_users = \
75
<<<<<<< HEAD
76
        PendingThirdPartyUser.objects.filter(third_party_identifier=eppn,
77
                                             provider='shibboleth')
78
=======
79
            PendingThirdPartyUser.objects.filter(third_party_identifier=eppn,
80
                                                 provider='shibboleth')
81
>>>>>>> astakos: Shibboleth EPPN migration functionality
82

    
83
    for pending in pending_users:
84
        msg = "Migrating pending user %s eppn (%s -> %s)"
85
        logger.info(msg, pending.email, eppn, remote_id)
86
        pending.third_party_identifier = remote_id
87
        pending.save()
88

    
89
    return remote_id
90

    
91

    
92
class Tokens:
93
    # these are mapped by the Shibboleth SP software
94
    SHIB_EPPN = "HTTP_EPPN"  # eduPersonPrincipalName
95
    SHIB_NAME = "HTTP_SHIB_INETORGPERSON_GIVENNAME"
96
    SHIB_SURNAME = "HTTP_SHIB_PERSON_SURNAME"
97
    SHIB_CN = "HTTP_SHIB_PERSON_COMMONNAME"
98
    SHIB_DISPLAYNAME = "HTTP_SHIB_INETORGPERSON_DISPLAYNAME"
99
    SHIB_EP_AFFILIATION = "HTTP_SHIB_EP_AFFILIATION"
100
    SHIB_SESSION_ID = "HTTP_SHIB_SESSION_ID"
101
    SHIB_MAIL = "HTTP_SHIB_MAIL"
102
    SHIB_REMOTE_USER = "HTTP_REMOTE_USER"
103

    
104

    
105
@requires_auth_provider('shibboleth')
106
@require_http_methods(["GET", "POST"])
107
@cookie_fix
108
def login(request,
109
          template='im/third_party_check_local.html',
110
          extra_context=None):
111

    
112
    init_third_party_session(request)
113
    extra_context = extra_context or {}
114

    
115
    tokens = request.META
116
    third_party_key = get_pending_key(request)
117

    
118
    shibboleth_headers = {}
119
    for token in dir(Tokens):
120
        if token == token.upper():
121
            shibboleth_headers[token] = request.META.get(getattr(Tokens,
122
                                                                 token),
123
                                                         'NOT_SET')
124
            # also include arbitrary shibboleth headers
125
            for key in request.META.keys():
126
                if key.startswith('HTTP_SHIB_'):
127
                    shibboleth_headers[key.replace('HTTP_', '')] = \
128
                        request.META.get(key)
129

    
130
    # log shibboleth headers
131
    # TODO: info -> debug
132
    logger.info("shibboleth request: %r" % shibboleth_headers)
133

    
134
    try:
135
        eppn = tokens.get(Tokens.SHIB_EPPN, None)
136
        user_id = tokens.get(Tokens.SHIB_REMOTE_USER)
137
        fullname, first_name, last_name, email = None, None, None, None
138
        if global_settings.DEBUG and not eppn:
139
            user_id = getattr(global_settings, 'SHIBBOLETH_TEST_REMOTE_USER',
140
                              None)
141
            eppn = getattr(global_settings, 'SHIBBOLETH_TEST_EPPN', None)
142
            fullname = getattr(global_settings, 'SHIBBOLETH_TEST_FULLNAME',
143
                               None)
144

    
145
        if not user_id:
146
            raise KeyError(_(astakos_messages.SHIBBOLETH_MISSING_USER_ID) % {
147
                'domain': settings.BASE_HOST,
148
                'contact_email': settings.CONTACT_EMAIL
149
            })
150
        if Tokens.SHIB_DISPLAYNAME in tokens:
151
            fullname = tokens[Tokens.SHIB_DISPLAYNAME]
152
        elif Tokens.SHIB_CN in tokens:
153
            fullname = tokens[Tokens.SHIB_CN]
154
        if Tokens.SHIB_NAME in tokens:
155
            first_name = tokens[Tokens.SHIB_NAME]
156
        if Tokens.SHIB_SURNAME in tokens:
157
            last_name = tokens[Tokens.SHIB_SURNAME]
158

    
159
        if fullname:
160
            splitted = fullname.split(' ', 1)
161
            if len(splitted) == 2:
162
                first_name, last_name = splitted
163
        fullname = '%s %s' % (first_name, last_name)
164

    
165
        if not any([first_name, last_name]) and \
166
                    settings.SHIBBOLETH_REQUIRE_NAME_INFO:
167
            raise KeyError(_(astakos_messages.SHIBBOLETH_MISSING_NAME))
168

    
169
    except KeyError, e:
170
        # invalid shibboleth headers, redirect to login, display message
171
        messages.error(request, e.message)
172
        return HttpResponseRedirect(login_url(request))
173

    
174
    if settings.SHIBBOLETH_MIGRATE_EPPN:
175
        migrate_eppn_to_remote_id(eppn, user_id)
176

    
177
    affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, 'Shibboleth')
178
    email = tokens.get(Tokens.SHIB_MAIL, '')
179
    provider_info = {'eppn': eppn, 'email': email, 'name': fullname,
180
                     'headers': shibboleth_headers, 'user_id': user_id}
181

    
182
    try:
183
        return handle_third_party_login(request, 'shibboleth',
184
                                        user_id, provider_info,
185
                                        affiliation, third_party_key)
186
    except AstakosUser.DoesNotExist, e:
187
        third_party_key = get_pending_key(request)
188
        user_info = {'affiliation': affiliation,
189
                     'first_name': first_name,
190
                     'last_name': last_name,
191
                     'email': email}
192
        return handle_third_party_signup(request, user_id, 'shibboleth',
193
                                         third_party_key,
194
                                         provider_info,
195
                                         user_info,
196
                                         template,
197
                                         extra_context)