Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (7.5 kB)

1 aba1e498 Antony Chazapis
# Copyright 2011-2012 GRNET S.A. All rights reserved.
2 5ce3ce4f Sofia Papagiannaki
#
3 64cd4730 Antony Chazapis
# Redistribution and use in source and binary forms, with or
4 64cd4730 Antony Chazapis
# without modification, are permitted provided that the following
5 64cd4730 Antony Chazapis
# conditions are met:
6 5ce3ce4f Sofia Papagiannaki
#
7 64cd4730 Antony Chazapis
#   1. Redistributions of source code must retain the above
8 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
9 64cd4730 Antony Chazapis
#      disclaimer.
10 5ce3ce4f Sofia Papagiannaki
#
11 64cd4730 Antony Chazapis
#   2. Redistributions in binary form must reproduce the above
12 64cd4730 Antony Chazapis
#      copyright notice, this list of conditions and the following
13 64cd4730 Antony Chazapis
#      disclaimer in the documentation and/or other materials
14 64cd4730 Antony Chazapis
#      provided with the distribution.
15 5ce3ce4f Sofia Papagiannaki
#
16 64cd4730 Antony Chazapis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 64cd4730 Antony Chazapis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 64cd4730 Antony Chazapis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 64cd4730 Antony Chazapis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 64cd4730 Antony Chazapis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 64cd4730 Antony Chazapis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 64cd4730 Antony Chazapis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 64cd4730 Antony Chazapis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 64cd4730 Antony Chazapis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 64cd4730 Antony Chazapis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 64cd4730 Antony Chazapis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 64cd4730 Antony Chazapis
# POSSIBILITY OF SUCH DAMAGE.
28 5ce3ce4f Sofia Papagiannaki
#
29 64cd4730 Antony Chazapis
# The views and conclusions contained in the software and
30 64cd4730 Antony Chazapis
# documentation are those of the authors and should not be
31 64cd4730 Antony Chazapis
# interpreted as representing official policies, either expressed
32 64cd4730 Antony Chazapis
# or implied, of GRNET S.A.
33 64cd4730 Antony Chazapis
34 c630fee6 Kostas Papadimitriou
import json
35 c630fee6 Kostas Papadimitriou
36 64cd4730 Antony Chazapis
from django.http import HttpResponseBadRequest
37 18ffbee1 Sofia Papagiannaki
from django.utils.translation import ugettext as _
38 18ffbee1 Sofia Papagiannaki
from django.contrib import messages
39 18ffbee1 Sofia Papagiannaki
from django.template import RequestContext
40 dda2e499 Sofia Papagiannaki
from django.views.decorators.http import require_http_methods
41 ef20ea07 Sofia Papagiannaki
from django.http import HttpResponseRedirect
42 ef20ea07 Sofia Papagiannaki
from django.core.urlresolvers import reverse
43 c0b26605 Sofia Papagiannaki
from django.core.exceptions import ImproperlyConfigured
44 d2633501 Kostas Papadimitriou
from django.shortcuts import get_object_or_404
45 d2633501 Kostas Papadimitriou
46 d2633501 Kostas Papadimitriou
from urlparse import urlunsplit, urlsplit
47 64cd4730 Antony Chazapis
48 aab4d540 Sofia Papagiannaki
from astakos.im.util import prepare_response, get_context
49 73fbaec4 Sofia Papagiannaki
from astakos.im.views import (
50 73fbaec4 Sofia Papagiannaki
    requires_anonymous, render_response, requires_auth_provider)
51 ef20ea07 Sofia Papagiannaki
from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION, BASEURL
52 ef20ea07 Sofia Papagiannaki
from astakos.im.models import AstakosUser, PendingThirdPartyUser
53 4e30244e Sofia Papagiannaki
from astakos.im.forms import LoginForm
54 4e30244e Sofia Papagiannaki
from astakos.im.activation_backends import get_backend, SimpleBackend
55 c630fee6 Kostas Papadimitriou
from astakos.im import auth_providers
56 d2633501 Kostas Papadimitriou
from astakos.im import settings
57 64cd4730 Antony Chazapis
58 ae497612 Olga Brani
import astakos.im.messages as astakos_messages
59 5ce3ce4f Sofia Papagiannaki
60 ef20ea07 Sofia Papagiannaki
import logging
61 ef20ea07 Sofia Papagiannaki
62 ef20ea07 Sofia Papagiannaki
logger = logging.getLogger(__name__)
63 ef20ea07 Sofia Papagiannaki
64 64cd4730 Antony Chazapis
class Tokens:
65 64cd4730 Antony Chazapis
    # these are mapped by the Shibboleth SP software
66 5ce3ce4f Sofia Papagiannaki
    SHIB_EPPN = "HTTP_EPPN"  # eduPersonPrincipalName
67 64cd4730 Antony Chazapis
    SHIB_NAME = "HTTP_SHIB_INETORGPERSON_GIVENNAME"
68 64cd4730 Antony Chazapis
    SHIB_SURNAME = "HTTP_SHIB_PERSON_SURNAME"
69 64cd4730 Antony Chazapis
    SHIB_CN = "HTTP_SHIB_PERSON_COMMONNAME"
70 64cd4730 Antony Chazapis
    SHIB_DISPLAYNAME = "HTTP_SHIB_INETORGPERSON_DISPLAYNAME"
71 64cd4730 Antony Chazapis
    SHIB_EP_AFFILIATION = "HTTP_SHIB_EP_AFFILIATION"
72 64cd4730 Antony Chazapis
    SHIB_SESSION_ID = "HTTP_SHIB_SESSION_ID"
73 ca828a10 Sofia Papagiannaki
    SHIB_MAIL = "HTTP_SHIB_MAIL"
74 64cd4730 Antony Chazapis
75 c630fee6 Kostas Papadimitriou
@requires_auth_provider('shibboleth', login=True)
76 dda2e499 Sofia Papagiannaki
@require_http_methods(["GET", "POST"])
77 ef20ea07 Sofia Papagiannaki
def login(
78 ef20ea07 Sofia Papagiannaki
    request,
79 d2633501 Kostas Papadimitriou
    template='im/third_party_check_local.html',
80 73fbaec4 Sofia Papagiannaki
    extra_context=None):
81 73fbaec4 Sofia Papagiannaki
82 ef20ea07 Sofia Papagiannaki
    extra_context = extra_context or {}
83 ef20ea07 Sofia Papagiannaki
84 4e30244e Sofia Papagiannaki
    tokens = request.META
85 d2633501 Kostas Papadimitriou
86 6c8a3f7c Sofia Papagiannaki
    try:
87 6c8a3f7c Sofia Papagiannaki
        eppn = tokens.get(Tokens.SHIB_EPPN)
88 6c8a3f7c Sofia Papagiannaki
        if not eppn:
89 e24d0e0d Kostas Papadimitriou
            raise KeyError(_(astakos_messages.SHIBBOLETH_MISSING_EPPN) % {
90 e24d0e0d Kostas Papadimitriou
                'domain': settings.BASEURL,
91 e24d0e0d Kostas Papadimitriou
                'contact_email': settings.DEFAULT_CONTACT_EMAIL
92 e24d0e0d Kostas Papadimitriou
            })
93 6c8a3f7c Sofia Papagiannaki
        if Tokens.SHIB_DISPLAYNAME in tokens:
94 6c8a3f7c Sofia Papagiannaki
            realname = tokens[Tokens.SHIB_DISPLAYNAME]
95 6c8a3f7c Sofia Papagiannaki
        elif Tokens.SHIB_CN in tokens:
96 6c8a3f7c Sofia Papagiannaki
            realname = tokens[Tokens.SHIB_CN]
97 6c8a3f7c Sofia Papagiannaki
        elif Tokens.SHIB_NAME in tokens and Tokens.SHIB_SURNAME in tokens:
98 6c8a3f7c Sofia Papagiannaki
            realname = tokens[Tokens.SHIB_NAME] + ' ' + tokens[Tokens.SHIB_SURNAME]
99 6c8a3f7c Sofia Papagiannaki
        else:
100 1d59653f Kostas Papadimitriou
            if settings.SHIBBOLETH_REQUIRE_NAME_INFO:
101 1d59653f Kostas Papadimitriou
                raise KeyError(_(astakos_messages.SHIBBOLETH_MISSING_NAME))
102 1d59653f Kostas Papadimitriou
            else:
103 1d59653f Kostas Papadimitriou
                realname = ''
104 6c8a3f7c Sofia Papagiannaki
    except KeyError, e:
105 d2633501 Kostas Papadimitriou
        # invalid shibboleth headers, redirect to login, display message
106 2e90e3ec Kostas Papadimitriou
        messages.error(request, e.message)
107 d2633501 Kostas Papadimitriou
        return HttpResponseRedirect(reverse('login'))
108 d2633501 Kostas Papadimitriou
109 6c8a3f7c Sofia Papagiannaki
    affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, '')
110 6c8a3f7c Sofia Papagiannaki
    email = tokens.get(Tokens.SHIB_MAIL, '')
111 4a1e3e53 Kostas Papadimitriou
    provider_info = {'eppn': eppn, 'email': email, 'name': realname}
112 d2633501 Kostas Papadimitriou
113 d2633501 Kostas Papadimitriou
    # an existing user accessed the view
114 d2633501 Kostas Papadimitriou
    if request.user.is_authenticated():
115 d2633501 Kostas Papadimitriou
        if request.user.has_auth_provider('shibboleth', identifier=eppn):
116 d2633501 Kostas Papadimitriou
            return HttpResponseRedirect(reverse('edit_profile'))
117 d2633501 Kostas Papadimitriou
118 d2633501 Kostas Papadimitriou
        # automatically add eppn provider to user
119 d2633501 Kostas Papadimitriou
        user = request.user
120 f432088a Kostas Papadimitriou
        if not request.user.can_add_auth_provider('shibboleth',
121 f432088a Kostas Papadimitriou
                                                  identifier=eppn):
122 4a1e3e53 Kostas Papadimitriou
            messages.error(request, _(astakos_messages.AUTH_PROVIDER_ADD_FAILED) +
123 4a1e3e53 Kostas Papadimitriou
                          u' ' + _(astakos_messages.AUTH_PROVIDER_ADD_EXISTS))
124 f432088a Kostas Papadimitriou
            return HttpResponseRedirect(reverse('edit_profile'))
125 f432088a Kostas Papadimitriou
126 c630fee6 Kostas Papadimitriou
        user.add_auth_provider('shibboleth', identifier=eppn,
127 4a1e3e53 Kostas Papadimitriou
                               affiliation=affiliation,
128 4a1e3e53 Kostas Papadimitriou
                               provider_info=provider_info)
129 4a1e3e53 Kostas Papadimitriou
        messages.success(request, astakos_messages.AUTH_PROVIDER_ADDED)
130 f432088a Kostas Papadimitriou
        return HttpResponseRedirect(reverse('edit_profile'))
131 d2633501 Kostas Papadimitriou
132 64cd4730 Antony Chazapis
    try:
133 d2633501 Kostas Papadimitriou
        # astakos user exists ?
134 d2633501 Kostas Papadimitriou
        user = AstakosUser.objects.get_auth_provider_user(
135 d2633501 Kostas Papadimitriou
            'shibboleth',
136 d2633501 Kostas Papadimitriou
            identifier=eppn
137 ef20ea07 Sofia Papagiannaki
        )
138 18ffbee1 Sofia Papagiannaki
        if user.is_active:
139 d2633501 Kostas Papadimitriou
            # authenticate user
140 c72d5a13 Kostas Papadimitriou
            response = prepare_response(request,
141 18ffbee1 Sofia Papagiannaki
                                    user,
142 18ffbee1 Sofia Papagiannaki
                                    request.GET.get('next'),
143 18ffbee1 Sofia Papagiannaki
                                    'renew' in request.GET)
144 c72d5a13 Kostas Papadimitriou
            response.set_cookie('astakos_last_login_method', 'local')
145 c72d5a13 Kostas Papadimitriou
            return response
146 18ffbee1 Sofia Papagiannaki
        else:
147 c4b1a172 Kostas Papadimitriou
            message = user.get_inactive_message()
148 1f3b4b39 Sofia Papagiannaki
            messages.error(request, message)
149 d2633501 Kostas Papadimitriou
            return HttpResponseRedirect(reverse('login'))
150 d2633501 Kostas Papadimitriou
151 8f5a3a06 Sofia Papagiannaki
    except AstakosUser.DoesNotExist, e:
152 c630fee6 Kostas Papadimitriou
        provider = auth_providers.get_provider('shibboleth')
153 c630fee6 Kostas Papadimitriou
        if not provider.is_available_for_create():
154 c4b1a172 Kostas Papadimitriou
            messages.error(request,
155 c49263c5 Kostas Papadimitriou
                           _(astakos_messages.AUTH_PROVIDER_INVALID_LOGIN))
156 c4b1a172 Kostas Papadimitriou
            return HttpResponseRedirect(reverse('login'))
157 c630fee6 Kostas Papadimitriou
158 d2633501 Kostas Papadimitriou
        # eppn not stored in astakos models, create pending profile
159 d2633501 Kostas Papadimitriou
        user, created = PendingThirdPartyUser.objects.get_or_create(
160 d2633501 Kostas Papadimitriou
            third_party_identifier=eppn,
161 3b258643 Kostas Papadimitriou
            provider='shibboleth'
162 d2633501 Kostas Papadimitriou
        )
163 d2633501 Kostas Papadimitriou
        # update pending user
164 d2633501 Kostas Papadimitriou
        user.realname = realname
165 d2633501 Kostas Papadimitriou
        user.affiliation = affiliation
166 d2633501 Kostas Papadimitriou
        user.email = email
167 3b258643 Kostas Papadimitriou
        user.info = json.dumps(provider_info)
168 d2633501 Kostas Papadimitriou
        user.generate_token()
169 d2633501 Kostas Papadimitriou
        user.save()
170 d2633501 Kostas Papadimitriou
171 d2633501 Kostas Papadimitriou
        extra_context['provider'] = 'shibboleth'
172 c49263c5 Kostas Papadimitriou
        extra_context['provider_title'] = provider.get_title_display
173 d2633501 Kostas Papadimitriou
        extra_context['token'] = user.token
174 c630fee6 Kostas Papadimitriou
        extra_context['signup_url'] = reverse('signup') + \
175 c49263c5 Kostas Papadimitriou
                                    "?third_party_token=%s" % user.token
176 c49263c5 Kostas Papadimitriou
        extra_context['add_url'] = reverse('index') + \
177 c49263c5 Kostas Papadimitriou
                                    "?key=%s#other-login-methods" % user.token
178 c49263c5 Kostas Papadimitriou
        extra_context['can_create'] = provider.is_available_for_create()
179 c49263c5 Kostas Papadimitriou
        extra_context['can_add'] = provider.is_available_for_add()
180 c49263c5 Kostas Papadimitriou
181 d2633501 Kostas Papadimitriou
182 ef20ea07 Sofia Papagiannaki
        return render_response(
183 ef20ea07 Sofia Papagiannaki
            template,
184 ef20ea07 Sofia Papagiannaki
            context_instance=get_context(request, extra_context)
185 ef20ea07 Sofia Papagiannaki
        )