Commit 3dc2371725386262a7002a8df73517b41724c6c1

Authored by Zambom
1 parent 2900c425

Password recovery implementation

amadeus/templates/recover_pass_email_template.html
1   -{% extends 'base.html' %}
2 1  
3 2 {% load i18n %}
4 3  
5   -{% block nav %}
6   -{% endblock %}
7   -
8   -{% block breadcrumbs %}
9   -{% endblock %}
10   -
11   -{% block sidebar %}
12   -{% endblock sidebar %}
13 4  
14 5 {% autoescape off %}
15 6  
... ... @@ -18,10 +9,9 @@
18 9 {% trans "Please go to the following page and choose a new password:" %}
19 10  
20 11 {% block reset_link %}
21   - {{ domain }}{% url 'users:reset_password_confirm' uidb64=uid token=token %}
22   - <!--This is the only change from ` django/contrib/admin/templates/registration/password_reset_subject.html`. the url name is commented out in urls.py section. The view associated with the url is going to described later in this post. -->
  12 + {{ domain }}
23 13 {% endblock %}
24 14  
25 15 {% blocktrans %}The {{ site_name }} team{% endblocktrans %}
26 16  
27   -{% endautoescape %}
28 17 \ No newline at end of file
  18 +{% endautoescape %}
... ...
users/forms.py
... ... @@ -197,4 +197,14 @@ class PassResetRequest(forms.Form):
197 197 except ValidationError:
198 198 self._errors['email'] = [_('You must insert an email address')]
199 199  
200   - return ValueError
201 200 \ No newline at end of file
  201 + return ValueError
  202 +
  203 +class SetPasswordForm(Validation):
  204 + is_edit = False
  205 +
  206 + new_password = forms.CharField(label=_('New Password'), widget = forms.PasswordInput(render_value=True), required = True)
  207 + password2 = forms.CharField(label = _('Confirm Password'), widget = forms.PasswordInput(render_value=True), required = True)
  208 +
  209 + class Meta:
  210 + model = User
  211 + fields = []
202 212 \ No newline at end of file
... ...
users/templates/users/new_password.html 0 → 100644
... ... @@ -0,0 +1,83 @@
  1 +{% extends 'base.html' %}
  2 +
  3 +{% load static i18n %}
  4 +{% load widget_tweaks %}
  5 +
  6 +{% block nav %}
  7 +{% endblock %}
  8 +
  9 +{% block breadcrumbs %}
  10 +{% endblock %}
  11 +
  12 +{% block sidebar %}
  13 +{% endblock sidebar %}
  14 +
  15 +{% block content %}
  16 + <div class="row">
  17 + <div class="col-sm-7 col-sm-offset-4 col-md-6 col-md-offset-4 col-xs-8 col-xs-offset-3 col-lg-6 col-lg-offset-4 col-xl-6 col-xl-offset-4 ">
  18 + <div class="col-sm-9 col-sm-offset-2 col-md-8 col-md-offset-2 col-xs-9 col-xs-offset-2 col-lg-8 col-lg-offset-2 col-xl-8 col-xl-offset-2">
  19 + <img src="{% static 'img/amadeus.png' %}" class="img-responsive center-block logo-login " alt="logo amadeus">
  20 + </div>
  21 + </div>
  22 + </div>
  23 + <div class="row">
  24 + <div class="col-lg-8 col-lg-offset-3 col-md-8 col-md-offset-3 col-sm-9 col-sm-offset-3 col-xs-10 col-xs-offset-2">
  25 + {% if messages %}
  26 + {% for message in messages %}
  27 + <div class="alert alert-{{ message.tags }} alert-dismissible" role="alert">
  28 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  29 + <span aria-hidden="true">&times;</span>
  30 + </button>
  31 + <p>{{ message }}</p>
  32 + </div>
  33 + {% endfor %}
  34 + {% endif %}
  35 + <div class="card">
  36 + <div class="card-block">
  37 + <div class="row">
  38 + <div class="col-md-12 text-center">
  39 + <h2 style="color:#43a251"><strong> {% trans 'Set new password' %} </strong></h2>
  40 + </div>
  41 + </div>
  42 +
  43 + <form id="form-reset" method="post" action="">
  44 + {% csrf_token %}
  45 + {% for field in form %}
  46 + <div class="col-md-10 col-md-offset-1">
  47 + <div class="form-group{% if form.has_error %} has-error {% endif %}">
  48 + {% if field.field.required %}
  49 + <label for="{{ field.auto_id }}" class="control-label">{{ field.label }} <span>*</span></label>
  50 + {% else %}
  51 + <label for="{{ field.auto_id }}" class="control-label">{{ field.label }}</label>
  52 + {% endif %}
  53 + {% render_field field class='form-control' %}
  54 + <span id="helpBlock" class="help-block">{{ field.help_text }}</span>
  55 + {% if field.errors %}
  56 + <div class="alert alert-danger alert-dismissible" role="alert">
  57 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  58 + <span aria-hidden="true">&times;</span>
  59 + </button>
  60 + <ul>
  61 + {% for error in field.errors %}
  62 + <li>{{ error }}</li>
  63 + {% endfor %}
  64 + </ul>
  65 + </div>
  66 + {% endif %}
  67 + </div>
  68 + </div>
  69 + {% endfor %}
  70 + </form>
  71 + <div class="row">
  72 + <div class="col-md-5 col-xs-6 col-sm-6 col-lg-5 col-lg-offset-1 col-md-offset-1 text-center">
  73 + <button type="submit" class="btn btn-success btn-raised btn-block" form="form-reset" style="position: initial;"> {% trans 'Reset' %} </button>
  74 + </div>
  75 + <div class="col-md-5 col-xs-6 col-sm-6 col-lg-5 text-center">
  76 + <a class="btn btn-default btn-raised btn-block" href="{% url 'users:login' %}" formaction="#" style="position: initial;">{% trans 'Back' %}</a>
  77 + </div>
  78 + </div>
  79 + </div>
  80 + </div>
  81 + </div>
  82 + </div>
  83 +{% endblock %}
0 84 \ No newline at end of file
... ...
users/urls.py
... ... @@ -8,7 +8,7 @@ urlpatterns = [
8 8 url(r'^logout/$', auth_views.logout, {'next_page': 'users:login'}, name='logout'),
9 9 url(r'^signup/$', views.RegisterUser.as_view(), name = 'signup'),
10 10 url(r'^forgot_password/$', views.ForgotPassword.as_view(), name = 'forgot_pass'),
11   - url(r'^reset_password_confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', views.ForgotPassword.as_view(), name = 'reset_password_confirm'),
  11 + url(r'^reset_password_confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', views.PasswordResetConfirmView.as_view(), name = 'reset_password_confirm'),
12 12 url(r'^$', views.UsersListView.as_view(), name = 'manage'),
13 13 url(r'^create/$', views.CreateView.as_view(), name = 'create'),
14 14 url(r'^edit/(?P<email>[\w.%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})/$', views.UpdateView.as_view(), name='update'),
... ...
users/views.py
... ... @@ -10,7 +10,7 @@ from rolepermissions.shortcuts import assign_role
10 10 from rolepermissions.verifications import has_role
11 11  
12 12 from .models import User
13   -from .forms import RegisterUserForm, ProfileForm, UserForm, ChangePassForm, PassResetRequest
  13 +from .forms import RegisterUserForm, ProfileForm, UserForm, ChangePassForm, PassResetRequest, SetPasswordForm
14 14  
15 15 #RECOVER PASS IMPORTS
16 16 from django.contrib.auth.tokens import default_token_generator
... ... @@ -27,11 +27,6 @@ from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnl
27 27  
28 28 # ================ ADMIN =======================
29 29  
30   -
31   -
32   -
33   -
34   -
35 30 # class View(LoginRequiredMixin, generic.DetailView):
36 31  
37 32 # #login_url = reverse_lazy("core:home")
... ... @@ -367,13 +362,16 @@ class ForgotPassword(generic.FormView):
367 362  
368 363 if users.exists():
369 364 for user in users:
  365 + uid = urlsafe_base64_encode(force_bytes(user.pk))
  366 + token = default_token_generator.make_token(user)
  367 +
370 368 c = {
  369 + 'request': request,
  370 + 'title': _('Recover Password'),
371 371 'email': user.email,
372   - 'domain': 'amadeus.com.br', #or your domain
  372 + 'domain': request.build_absolute_uri(reverse("users:reset_password_confirm", kwargs = {'uidb64': uid, 'token':token})), #or your domain
373 373 'site_name': 'Amadeus',
374   - 'uid': urlsafe_base64_encode(force_bytes(user.pk)),
375 374 'user': user,
376   - 'token': default_token_generator.make_token(user),
377 375 'protocol': 'http',
378 376 }
379 377  
... ... @@ -388,7 +386,7 @@ class ForgotPassword(generic.FormView):
388 386 send_mail(subject, email, settings.DEFAULT_FROM_EMAIL , [user.email], fail_silently=False)
389 387  
390 388 result = self.form_valid(form)
391   - messages.success(request, _("An email has been sent to the informed address. Please check its inbox to continue reseting password."))
  389 + messages.success(request, _("Soon you'll receive an email with instructions to set your new password. If you don't receive it in 24 hours, please check your spam box."))
392 390  
393 391 return result
394 392  
... ... @@ -397,6 +395,46 @@ class ForgotPassword(generic.FormView):
397 395  
398 396 return result
399 397  
  398 +class PasswordResetConfirmView(generic.FormView):
  399 + template_name = "users/new_password.html"
  400 + success_url = reverse_lazy('users:login')
  401 + form_class = SetPasswordForm
  402 +
  403 + def get_context_data(self, **kwargs):
  404 + context = super(PasswordResetConfirmView, self).get_context_data(**kwargs)
  405 + context['title'] = _('Reset Password')
  406 +
  407 + return context
  408 +
  409 + def post(self, request, uidb64=None, token=None, *arg, **kwargs):
  410 + form = self.get_form()
  411 +
  412 + assert uidb64 is not None and token is not None
  413 +
  414 + try:
  415 + uid = urlsafe_base64_decode(uidb64)
  416 + user = User._default_manager.get(pk=uid)
  417 + except (TypeError, ValueError, OverflowError, User.DoesNotExist):
  418 + user = None
  419 +
  420 + if user is not None and default_token_generator.check_token(user, token):
  421 + if form.is_valid():
  422 + new_password = form.cleaned_data['new_password']
  423 +
  424 + user.set_password(new_password)
  425 + user.save()
  426 +
  427 + messages.success(request, 'Password reset successfully.')
  428 +
  429 + return self.form_valid(form)
  430 + else:
  431 + messages.error(request, 'We were not able to reset your password.')
  432 + return self.form_invalid(form)
  433 + else:
  434 + messages.error(request,'The reset password link is no longer valid.')
  435 + return self.form_invalid(form)
  436 +
  437 +
400 438 def login(request):
401 439 context = {}
402 440 context['title'] = _('Log In')
... ...