AiC and REST login
[myslice.git] / portal / django_passresetview.py
1 # -*- coding: utf-8 -*-
2 #
3 # portal/views.py: views for the portal application
4 # This file is part of the Manifold project.
5 #
6 # Author:
7 #   Mohammed Yasin Rahman <mohammed-yasin.rahman@lip6.fr>
8 # Copyright 2014, UPMC Sorbonne Universités / LIP6
9 #
10 # This program is free software; you can redistribute it and/or modify it under
11 # the terms of the GNU General Public License as published by the Free Software
12 # Foundation; either version 3, or (at your option) any later version.
13
14 # This program is distributed in the hope that it will be useful, but WITHOUT
15 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17 # details.
18 #   
19 # You should have received a copy of the GNU General Public License along with
20 # this program; see the file COPYING.  If not, write to the Free Software
21 # Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23
24
25
26
27 """
28 View Description:
29
30 Allows a user to reset their password by generating a one-time use link that can be used to reset the password, and sending that link to the user's 
31 registered email address.
32
33 If the email address provided does not exist in the system, this view won't send an email, but the user won't receive any error message either. 
34 This prevents information leaking to potential attackers. If you want to provide an error message in this case, you can subclass PasswordResetForm 
35 and use the password_reset_form argument.
36
37 Users flagged with an unusable password - see set_unusable_password() - aren't allowed to request a password reset to prevent misuse when using an external 
38 authentication source like LDAP. Note that they won't receive any error message since this would expose their account's existence but no mail will be sent either.
39
40 More Detail: https://docs.djangoproject.com/en/dev/topics/auth/default/#topics-auth-creating-users
41 """
42
43
44
45 try:
46     from urllib.parse import urlparse, urlunparse
47 except ImportError:     # Python 2
48     from urlparse import urlparse, urlunparse
49
50 from django.conf import settings
51 from django.core.urlresolvers import reverse
52 from django.http import HttpResponseRedirect, QueryDict
53 from django.template.response import TemplateResponse
54 from django.utils.http import base36_to_int, is_safe_url
55 from django.utils.translation import ugettext as _
56 from django.shortcuts import resolve_url
57 from django.views.decorators.debug import sensitive_post_parameters
58 from django.views.decorators.cache import never_cache
59 from django.views.decorators.csrf import csrf_protect
60
61 # Avoid shadowing the login() and logout() views below.
62 from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login, logout as auth_logout, get_user_model
63 from django.contrib.auth.decorators import login_required
64 from portal.forms import PasswordResetForm, SetPasswordForm
65 from django.contrib.auth.tokens import default_token_generator
66 from django.contrib.sites.models import get_current_site
67 from django.contrib.auth.hashers import identify_hasher
68
69 ##
70 import os.path, re
71 import json
72
73 from random                     import choice
74
75 from django.contrib             import messages
76 from django.views.generic       import View
77 from django.shortcuts           import render
78 from django.http                        import HttpResponse, HttpResponseRedirect
79
80 from unfold.loginrequired       import FreeAccessView
81 from ui.topmenu                 import topmenu_items_live
82
83 from manifoldapi.manifoldapi    import execute_admin_query
84 from manifold.core.query        import Query
85 from portal.actions             import manifold_update_user
86
87 from portal.forms               import PassResetForm
88 from portal.actions             import manifold_update_user
89
90 from myslice.theme import ThemeView
91
92 # 4 views for password reset:
93 # - password_reset sends the mail
94 # - password_reset_done shows a success message for the above
95 # - password_reset_confirm checks the link the user clicked and
96 #   prompts for a new password
97 # - password_reset_complete shows a success message for the above
98
99 @csrf_protect
100 def password_reset(request, is_admin_site=False,
101                    template_name='password_reset_form.html',
102                    email_template_name='password_reset_email.html',
103                    subject_template_name='registration/password_reset_subject.txt',
104                    password_reset_form=PasswordResetForm,
105                    token_generator=default_token_generator,
106                    post_reset_redirect=None,
107                    from_email=None,
108                    current_app=None,
109                    extra_context=None):
110     
111     themeview = ThemeView()
112     themeview.template_name = template_name
113     
114     if post_reset_redirect is None:
115         post_reset_redirect = reverse('portal.django_passresetview.password_reset_done')
116     if request.method == "POST":
117         form = password_reset_form(request.POST)
118         if form.is_valid():
119
120             ### email check in manifold DB ###
121             email = form.cleaned_data['email'].lower() # email inserted on the form
122             user_query  = Query().get('local:user').select('user_id','email')
123             user_details = execute_admin_query(request, user_query)
124             flag = 0
125             for user_detail in user_details:
126                 if user_detail['email']==email:
127                     flag = 1
128                     break
129                     
130             if flag == 0:
131                 messages.error(request, 'Sorry, this email is not registered.')
132                 context = {
133                     'form': form,
134                     'theme': themeview.theme
135                 }   
136                 return TemplateResponse(request, themeview.template, context,current_app=current_app)
137
138             ### end of email check in manifold  ### 
139
140             opts = {
141                 'use_https': request.is_secure(),
142                 'token_generator': token_generator,
143                 'from_email': from_email,
144                 'email_template_name': email_template_name,
145                 'subject_template_name': subject_template_name,
146                 'request': request,
147             }
148             if is_admin_site:
149                 opts = dict(opts, domain_override=request.get_host())
150             form.save(**opts)
151             return HttpResponseRedirect(post_reset_redirect)
152     else:
153         form = password_reset_form()
154     context = {
155         'form': form,
156         'theme': themeview.theme
157     }
158     if extra_context is not None:
159         context.update(extra_context)
160     return TemplateResponse(request, themeview.template, context,
161                             current_app=current_app)
162
163
164 def password_reset_done(request,
165                         template_name='password_reset_done.html',
166                         current_app=None, extra_context=None):
167     themeview = ThemeView()
168     themeview.template_name = template_name
169     context = {
170                'theme' : themeview.theme
171     }
172     if extra_context is not None:
173         context.update(extra_context)
174     return TemplateResponse(request, themeview.template, context,
175                             current_app=current_app)
176
177
178 # Doesn't need csrf_protect since no-one can guess the URL
179 @sensitive_post_parameters()
180 @never_cache
181 def password_reset_confirm(request, uidb36=None, token=None,
182                            template_name='password_reset_confirm.html',
183                            token_generator=default_token_generator,
184                            set_password_form=SetPasswordForm,
185                            post_reset_redirect=None,
186                            current_app=None, extra_context=None):
187     """
188     View that checks the hash in a password reset link and presents a
189     form for entering a new password.
190     """
191     themeview = ThemeView()
192     themeview.template_name = template_name
193     
194     UserModel = get_user_model()
195     assert uidb36 is not None and token is not None  # checked by URLconf
196     if post_reset_redirect is None:
197         post_reset_redirect = reverse('portal.django_passresetview.password_reset_complete')
198     try:
199         uid_int = base36_to_int(uidb36)
200         user = UserModel._default_manager.get(pk=uid_int)
201     except (ValueError, OverflowError, UserModel.DoesNotExist):
202         user = None
203
204     if user is not None and token_generator.check_token(user, token):
205         validlink = True
206         if request.method == 'POST':
207             form = set_password_form(user, request.POST)
208             if form.is_valid():
209
210                 ### manifold pass update ###
211                 #password = form.cleaned_data('password1')
212                 password=request.POST['new_password1']
213                 #user_query  = Query().get('local:user').select('user_id','email','password')
214                 #user_details = execute_admin_query(request, user_query)
215                 #for user_detail in user_details:
216                 #    if user_detail['email'] == user.email:
217                 #        user_detail['password'] = password
218                 #updating password in local:user
219                 user_params = { 'password': password}
220                 manifold_update_user(request,user.email,user_params)    
221                 ### end of manifold pass update ###            
222     
223     
224                 form.save()
225                 return HttpResponseRedirect(post_reset_redirect)
226         else:
227             form = set_password_form(None)
228     else:
229         validlink = False
230         form = None
231     context = {
232         'form': form,
233         'validlink': validlink,
234         'theme' : themeview.theme
235     }
236     if extra_context is not None:
237         context.update(extra_context)
238     return TemplateResponse(request, themeview.template, context,
239                             current_app=current_app)
240
241
242 def password_reset_complete(request,
243                             template_name='password_reset_complete.html',
244                             current_app=None, extra_context=None):
245     themeview = ThemeView()
246     themeview.template_name = template_name
247     context = {
248         'login_url': resolve_url(settings.LOGIN_URL),
249         'theme' : themeview.theme
250     }
251     if extra_context is not None:
252         context.update(extra_context)
253     return TemplateResponse(request, themeview.template, context,
254                             current_app=current_app)
255
256