Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (7 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.http import HttpResponseBadRequest
35
from django.utils.translation import ugettext as _
36
from django.contrib import messages
37
from django.template import RequestContext
38
from django.views.decorators.http import require_http_methods
39
from django.db.models import Q
40
from django.core.exceptions import ValidationError
41
from django.http import HttpResponseRedirect
42
from django.core.urlresolvers import reverse
43
from urlparse import urlunsplit, urlsplit
44
from django.utils.http import urlencode
45

    
46
from astakos.im.util import prepare_response, get_context, get_invitation
47
from astakos.im.views import requires_anonymous, render_response
48
from astakos.im.settings import ENABLE_LOCAL_ACCOUNT_MIGRATION, BASEURL
49

    
50
from astakos.im.models import AstakosUser, PendingThirdPartyUser
51
from astakos.im.forms import LoginForm
52
from astakos.im.activation_backends import get_backend, SimpleBackend
53

    
54
import logging
55

    
56
logger = logging.getLogger(__name__)
57

    
58
class Tokens:
59
    # these are mapped by the Shibboleth SP software
60
    SHIB_EPPN = "HTTP_EPPN" # eduPersonPrincipalName
61
    SHIB_NAME = "HTTP_SHIB_INETORGPERSON_GIVENNAME"
62
    SHIB_SURNAME = "HTTP_SHIB_PERSON_SURNAME"
63
    SHIB_CN = "HTTP_SHIB_PERSON_COMMONNAME"
64
    SHIB_DISPLAYNAME = "HTTP_SHIB_INETORGPERSON_DISPLAYNAME"
65
    SHIB_EP_AFFILIATION = "HTTP_SHIB_EP_AFFILIATION"
66
    SHIB_SESSION_ID = "HTTP_SHIB_SESSION_ID"
67
    SHIB_MAIL = "HTTP_SHIB_MAIL"
68

    
69
@require_http_methods(["GET", "POST"])
70
@requires_anonymous
71
def login(
72
    request,
73
    on_login_template='im/login.html',
74
    on_signup_template='im/third_party_check_local.html',
75
    extra_context=None
76
):
77
    extra_context = extra_context or {}
78

    
79
    tokens = request.META
80
    
81
    try:
82
        eppn = tokens[Tokens.SHIB_EPPN]
83
        if not eppn:
84
            raise KeyError
85
    except KeyError:
86
        return HttpResponseBadRequest("Missing unique token in request")
87
    
88
    if Tokens.SHIB_DISPLAYNAME in tokens:
89
        realname = tokens[Tokens.SHIB_DISPLAYNAME]
90
    elif Tokens.SHIB_CN in tokens:
91
        realname = tokens[Tokens.SHIB_CN]
92
    elif Tokens.SHIB_NAME in tokens and Tokens.SHIB_SURNAME in tokens:
93
        realname = tokens[Tokens.SHIB_NAME] + ' ' + tokens[Tokens.SHIB_SURNAME]
94
    else:
95
        return HttpResponseBadRequest("Missing user name in request")
96
    
97
    affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, '')
98
    email = tokens.get(Tokens.SHIB_MAIL, '')
99
        
100
    try:
101
        user = AstakosUser.objects.get(
102
            provider='shibboleth',
103
            third_party_identifier=eppn
104
        )
105
        if user.is_active:
106
            return prepare_response(request,
107
                                    user,
108
                                    request.GET.get('next'),
109
                                    'renew' in request.GET)
110
        else:
111
            message = _('Inactive account')
112
            messages.add_message(request, messages.ERROR, message)
113
            return render_response(on_login_template,
114
                                   login_form = LoginForm(request=request),
115
                                   context_instance=RequestContext(request))
116
    except AstakosUser.DoesNotExist, e:
117
        # First time
118
        try:
119
            user, created = PendingThirdPartyUser.objects.get_or_create(
120
                third_party_identifier=eppn,
121
                provider='shibboleth',
122
                defaults=dict(
123
                    realname=realname,
124
                    affiliation=affiliation,
125
                    email=email
126
                )
127
            )
128
            user.save()
129
        except BaseException, e:
130
            logger.exception(e)
131
            template = on_login_template
132
            extra_context['login_form'] = LoginForm(request=request)
133
            messages.error(request, _('Something went wrong.'))
134
        else:
135
            if not ENABLE_LOCAL_ACCOUNT_MIGRATION:
136
                url = reverse(
137
                    'astakos.im.target.shibboleth.signup'
138
                )
139
                parts = list(urlsplit(url))
140
                parts[3] = urlencode({'key': user.username})
141
                url = urlunsplit(parts)
142
                return HttpResponseRedirect(url)
143
            else:
144
                template = on_signup_template
145
                extra_context['key'] = user.username
146
        
147
        extra_context['provider']='shibboleth'
148
        return render_response(
149
            template,
150
            context_instance=get_context(request, extra_context)
151
        )
152

    
153
@require_http_methods(["GET"])
154
@requires_anonymous
155
def signup(
156
    request,
157
    backend=None,
158
    on_creation_template='im/third_party_registration.html',
159
    extra_context=None
160
):
161
    extra_context = extra_context or {}
162
    username = request.GET.get('key')
163
    if not username:
164
        return HttpResponseBadRequest(_('Missing key parameter.'))
165
    try:
166
        pending = PendingThirdPartyUser.objects.get(username=username)
167
    except BaseException, e:
168
        logger.exception(e)
169
        return HttpResponseBadRequest(_('Invalid key.'))
170
    else:
171
        d = pending.__dict__
172
        d.pop('_state', None)
173
        d.pop('id', None)
174
        user = AstakosUser(**d)
175
        try:
176
            backend = backend or get_backend(request)
177
        except ImproperlyConfigured, e:
178
            messages.error(request, e)
179
        else:
180
            extra_context['form'] = backend.get_signup_form(
181
                provider='shibboleth',
182
                instance=user
183
            )
184
    extra_context['provider']='shibboleth'
185
    return render_response(
186
            on_creation_template,
187
            context_instance=get_context(request, extra_context)
188
    )