05f86c539e9a600664f283db31ae24cd3e67c1cb
[astakos] / snf-astakos-app / astakos / im / target / shibboleth.py
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     login_template='im/login.html',
74     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.get(Tokens.SHIB_EPPN)
83 #         if not eppn:
84 #             raise KeyError(_('Missing unique token in request'))
85 #         if Tokens.SHIB_DISPLAYNAME in tokens:
86 #             realname = tokens[Tokens.SHIB_DISPLAYNAME]
87 #         elif Tokens.SHIB_CN in tokens:
88 #             realname = tokens[Tokens.SHIB_CN]
89 #         elif Tokens.SHIB_NAME in tokens and Tokens.SHIB_SURNAME in tokens:
90 #             realname = tokens[Tokens.SHIB_NAME] + ' ' + tokens[Tokens.SHIB_SURNAME]
91 #         else:
92 #             raise KeyError(_('Missing user name in request'))
93 #     except KeyError, e:
94 #         extra_context['login_form'] = LoginForm(request=request)
95 #         messages.error(request, e)
96 #         return render_response(
97 #             login_template,
98 #             context_instance=get_context(request, extra_context)
99 #         )
100 #     
101 #     affiliation = tokens.get(Tokens.SHIB_EP_AFFILIATION, '')
102 #     email = tokens.get(Tokens.SHIB_MAIL, '')
103     
104     eppn, realname, affiliation, email = 'shibboleth1', 'shib Boleth', '', '' 
105     
106     try:
107         user = AstakosUser.objects.get(
108             provider='shibboleth',
109             third_party_identifier=eppn
110         )
111         if user.is_active:
112             return prepare_response(request,
113                                     user,
114                                     request.GET.get('next'),
115                                     'renew' in request.GET)
116         elif not user.activation_sent:
117             message = _('Your request is pending activation')
118             messages.error(request, message)
119         else:
120             urls = {}
121             urls['send_activation'] = reverse(
122                 'send_activation',
123                 kwargs={'user_id':user.id}
124             )
125             urls['signup'] = reverse(
126                 'shibboleth_signup',
127                 args= [user.username]
128             )   
129             message = _(
130                 'You have not followed the activation link. \
131                 <a href="%(send_activation)s">Resend activation email?</a> or \
132                 <a href="%(signup)s">Provide new email?</a>' % urls
133             )
134             messages.error(request, message)
135         return render_response(login_template,
136                                login_form = LoginForm(request=request),
137                                context_instance=RequestContext(request))
138     except AstakosUser.DoesNotExist, e:
139         # First time
140         try:
141             user, created = PendingThirdPartyUser.objects.get_or_create(
142                 third_party_identifier=eppn,
143                 provider='shibboleth',
144                 defaults=dict(
145                     realname=realname,
146                     affiliation=affiliation,
147                     email=email
148                 )
149             )
150             user.save()
151         except BaseException, e:
152             logger.exception(e)
153             template = login_template
154             extra_context['login_form'] = LoginForm(request=request)
155             messages.error(request, _('Something went wrong.'))
156         else:
157             if not ENABLE_LOCAL_ACCOUNT_MIGRATION:
158                 url = reverse(
159                     'shibboleth_signup',
160                     args= [user.username]
161                 )
162                 return HttpResponseRedirect(url)
163             else:
164                 template = signup_template
165                 extra_context['username'] = user.username
166         
167         extra_context['provider']='shibboleth'
168         return render_response(
169             template,
170             context_instance=get_context(request, extra_context)
171         )
172
173 @require_http_methods(["GET"])
174 @requires_anonymous
175 def signup(
176     request,
177     username,
178     backend=None,
179     on_creation_template='im/third_party_registration.html',
180     extra_context=None
181 ):
182     extra_context = extra_context or {}
183     try:
184         pending = PendingThirdPartyUser.objects.get(username=username)
185     except BaseException, e:
186         try:
187             user = AstakosUser.objects.get(username=username)
188         except BaseException, e:
189             logger.exception(e)
190             return HttpResponseBadRequest(_('Invalid key.'))
191     else:
192         d = pending.__dict__
193         d.pop('_state', None)
194         d.pop('id', None)
195         user = AstakosUser(**d)
196     try:
197         backend = backend or get_backend(request)
198     except ImproperlyConfigured, e:
199         messages.error(request, e)
200     else:
201         extra_context['form'] = backend.get_signup_form(
202             provider='shibboleth',
203             instance=user
204         )
205     extra_context['provider']='shibboleth'
206     return render_response(
207             on_creation_template,
208             context_instance=get_context(request, extra_context)
209     )