$ apt-get install python-django-south
* init django
+** when django prompts for creating an admin account, create it and
+** keep the username and password safe
$ ./manage.py syncdb
$ ./manage.py migrate
EMAIL_PORT = 25
EMAIL_USE_TLS = False
+# use the email for debugging purpose
+# turn on debugging:
+# python -m smtpd -n -c DebuggingServer localhost:1025
+
+if DEBUG:
+ EMAIL_HOST = 'localhost'
+ EMAIL_PORT = 1025
+ EMAIL_HOST_USER = ''
+ EMAIL_HOST_PASSWORD = ''
+ EMAIL_USE_TLS = False
+ DEFAULT_FROM_EMAIL = 'testing@example.com'
+
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
# managing database migrations
'south',
# Uncomment the next line to enable the admin:
- # 'django.contrib.admin',
+ 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
'portal',
}
}
-AUTHENTICATION_BACKENDS = ( 'auth.manifoldbackend.ManifoldBackend', )
+AUTHENTICATION_BACKENDS = ( 'auth.manifoldbackend.ManifoldBackend','django.contrib.auth.backends.ModelBackend' )
### the view to redirect malformed (i.e. with a wrong CSRF) incoming requests
# without this setting django will return a 403 forbidden error, which is fine
from django.conf.urls import patterns, include, url
from django.conf import settings
+from django.contrib import admin
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# where to redirect when login is required
# might need another one ?
the_login_view=home_view
-
+admin.autodiscover()
urls = [
'',
# Examples:
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
- # url(r'^admin/', include(admin.site.urls)),
+ url(r'^admin/', include(admin.site.urls)),
#
# default / view
(r'^/?$', the_default_view),
pub_key_list = []
for platform_detail in platform_details:
- if 'sfa' in platform_detail['gateway_type'] and platform_detail['disabled']==0:
+ if 'sfa' in platform_detail['gateway_type']:
total_platform = platform_detail['platform']
total_platform_list.append(total_platform)
user_config['config']= '{"firstname":"' + edited_first_name + '", "lastname":"'+ edited_last_name + '", "authority": "Unknown Authority"}'
user_params = {'config': user_config['config']}
# updating config local:user in manifold
- manifold_update_user(request,user_params)
+ manifold_update_user(request, request.user.email,user_params)
# this will be depricated, we will show the success msg in same page
# Redirect to same page with success message
messages.success(request, 'Sucess: First Name and Last Name Updated.')
user_pass['password'] = edited_password
#updating password in local:user
user_params = { 'password': user_pass['password']}
- manifold_update_user(request,user_params)
+ manifold_update_user(request,request.user.email,user_params)
# return HttpResponse('Success: Password Changed!!')
messages.success(request, 'Sucess: Password Updated.')
return HttpResponseRedirect("/portal/account/")
result, = results
return result['email']
-def manifold_update_user(request, user_params):
+def manifold_update_user(request, email, user_params):
# user_params: password, config e.g.,
- query = Query.update('local:user').filter_by('email', '==', request.user.email).set(user_params).select('email')
- results = execute_query(request,query)
+ query = Query.update('local:user').filter_by('email', '==', email).set(user_params).select('email')
+ results = execute_admin_query(request,query)
# NOTE: results remains empty and goes to Exception. However, it updates the manifold DB.
# That's why I commented the exception part. -- Yasin
#if not results:
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# portal/views.py: views for the portal application
+# This file is part of the Manifold project.
+#
+# Author:
+# Mohammed Yasin Rahman <mohammed-yasin.rahman@lip6.fr>
+# Copyright 2014, UPMC Sorbonne Universités / LIP6
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; see the file COPYING. If not, write to the Free Software
+# Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+
+
+
+"""
+View Description:
+
+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
+registered email address.
+
+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.
+This prevents information leaking to potential attackers. If you want to provide an error message in this case, you can subclass PasswordResetForm
+and use the password_reset_form argument.
+
+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
+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.
+
+More Detail: https://docs.djangoproject.com/en/dev/topics/auth/default/#topics-auth-creating-users
+"""
+
+
+
+try:
+ from urllib.parse import urlparse, urlunparse
+except ImportError: # Python 2
+ from urlparse import urlparse, urlunparse
+
+from django.conf import settings
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect, QueryDict
+from django.template.response import TemplateResponse
+from django.utils.http import base36_to_int, is_safe_url
+from django.utils.translation import ugettext as _
+from django.shortcuts import resolve_url
+from django.views.decorators.debug import sensitive_post_parameters
+from django.views.decorators.cache import never_cache
+from django.views.decorators.csrf import csrf_protect
+
+# Avoid shadowing the login() and logout() views below.
+from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login, logout as auth_logout, get_user_model
+from django.contrib.auth.decorators import login_required
+from portal.forms import PasswordResetForm, SetPasswordForm
+from django.contrib.auth.tokens import default_token_generator
+from django.contrib.sites.models import get_current_site
+from django.contrib.auth.hashers import UNUSABLE_PASSWORD, identify_hasher
+
+##
+import os.path, re
+import json
+
+from random import choice
+
+from django.core.mail import send_mail
+from django.contrib import messages
+from django.views.generic import View
+from django.shortcuts import render
+from django.http import HttpResponse, HttpResponseRedirect
+
+from unfold.loginrequired import FreeAccessView
+from ui.topmenu import topmenu_items_live
+
+from manifold.manifoldapi import execute_admin_query
+from manifold.core.query import Query
+from portal.actions import manifold_update_user
+
+from portal.forms import PassResetForm
+from portal.actions import manifold_update_user
+
+
+
+# 4 views for password reset:
+# - password_reset sends the mail
+# - password_reset_done shows a success message for the above
+# - password_reset_confirm checks the link the user clicked and
+# prompts for a new password
+# - password_reset_complete shows a success message for the above
+
+@csrf_protect
+def password_reset(request, is_admin_site=False,
+ template_name='registration/password_reset_form.html',
+ email_template_name='registration/password_reset_email.html',
+ subject_template_name='registration/password_reset_subject.txt',
+ password_reset_form=PasswordResetForm,
+ token_generator=default_token_generator,
+ post_reset_redirect=None,
+ from_email=None,
+ current_app=None,
+ extra_context=None):
+ if post_reset_redirect is None:
+ post_reset_redirect = reverse('portal.django_passresetview.password_reset_done')
+ if request.method == "POST":
+ form = password_reset_form(request.POST)
+ if form.is_valid():
+
+ ### email check in manifold DB ###
+ email = form.cleaned_data['email'] # email inserted on the form
+ user_query = Query().get('local:user').select('user_id','email')
+ user_details = execute_admin_query(request, user_query)
+ flag = 0
+ for user_detail in user_details:
+ if user_detail['email']==email:
+ flag = 1
+ break
+
+ if flag == 0:
+ messages.error(request, 'Sorry, this email is not registered.')
+ return render(request, 'registration/password_reset_form.html', {
+ 'form': form,
+ })
+ ### end of email check in manifold ###
+
+ opts = {
+ 'use_https': request.is_secure(),
+ 'token_generator': token_generator,
+ 'from_email': from_email,
+ 'email_template_name': email_template_name,
+ 'subject_template_name': subject_template_name,
+ 'request': request,
+ }
+ if is_admin_site:
+ opts = dict(opts, domain_override=request.get_host())
+ form.save(**opts)
+ return HttpResponseRedirect(post_reset_redirect)
+ else:
+ form = password_reset_form()
+ context = {
+ 'form': form,
+ }
+ if extra_context is not None:
+ context.update(extra_context)
+ return TemplateResponse(request, template_name, context,
+ current_app=current_app)
+
+
+def password_reset_done(request,
+ template_name='registration/password_reset_done.html',
+ current_app=None, extra_context=None):
+ context = {}
+ if extra_context is not None:
+ context.update(extra_context)
+ return TemplateResponse(request, template_name, context,
+ current_app=current_app)
+
+
+# Doesn't need csrf_protect since no-one can guess the URL
+@sensitive_post_parameters()
+@never_cache
+def password_reset_confirm(request, uidb36=None, token=None,
+ template_name='registration/password_reset_confirm.html',
+ token_generator=default_token_generator,
+ set_password_form=SetPasswordForm,
+ post_reset_redirect=None,
+ current_app=None, extra_context=None):
+ """
+ View that checks the hash in a password reset link and presents a
+ form for entering a new password.
+ """
+ UserModel = get_user_model()
+ assert uidb36 is not None and token is not None # checked by URLconf
+ if post_reset_redirect is None:
+ post_reset_redirect = reverse('portal.django_passresetview.password_reset_complete')
+ try:
+ uid_int = base36_to_int(uidb36)
+ user = UserModel._default_manager.get(pk=uid_int)
+ except (ValueError, OverflowError, UserModel.DoesNotExist):
+ user = None
+
+ if user is not None and token_generator.check_token(user, token):
+ validlink = True
+ if request.method == 'POST':
+ form = set_password_form(user, request.POST)
+ if form.is_valid():
+
+ ### manifold pass update ###
+ #password = form.cleaned_data('password1')
+ password=request.POST['new_password1']
+ user_query = Query().get('local:user').select('user_id','email','password')
+ user_details = execute_admin_query(request, user_query)
+ for user_detail in user_details:
+ if user_detail['email'] == user.email:
+ user_detail['password'] = password
+ #updating password in local:user
+ user_params = { 'password': user_detail['password']}
+ manifold_update_user(request,user.email,user_params)
+ ### end of manifold pass update ###
+
+
+ form.save()
+ return HttpResponseRedirect(post_reset_redirect)
+ else:
+ form = set_password_form(None)
+ else:
+ validlink = False
+ form = None
+ context = {
+ 'form': form,
+ 'validlink': validlink,
+ }
+ if extra_context is not None:
+ context.update(extra_context)
+ return TemplateResponse(request, template_name, context,
+ current_app=current_app)
+
+
+def password_reset_complete(request,
+ template_name='registration/password_reset_complete.html',
+ current_app=None, extra_context=None):
+ context = {
+ 'login_url': resolve_url(settings.LOGIN_URL)
+ }
+ if extra_context is not None:
+ context.update(extra_context)
+ return TemplateResponse(request, template_name, context,
+ current_app=current_app)
+
+
#from crispy_forms.helper import FormHelper
#from crispy_forms.layout import Submit
from django.utils.translation import ugettext_lazy as _
+from django.contrib.auth.tokens import default_token_generator
+from django.contrib.auth import authenticate, get_user_model
+from django.contrib.auth.hashers import UNUSABLE_PASSWORD, identify_hasher
+from django.contrib.sites.models import get_current_site
+from django.utils.http import int_to_base36
+from django.template import loader
+
+
+
# xxx painful, but...
# bootstrap3 requires the <input> fields to be tagged class='form-control'
widget = forms.Select(attrs={'class':'form-control'}),
choices = authority_hrn,
help_text = "An authority responsible for vetting your slice")
+
+
+class PasswordResetForm(forms.Form):
+ error_messages = {
+ 'unknown': _("That email address doesn't have an associated "
+ "user account. Are you sure you've registered?"),
+ 'unusable': _("The user account associated with this email "
+ "address cannot reset the password."),
+ }
+ email = forms.EmailField(label=_("Email"), max_length=254)
+
+ def clean_email(self):
+ """
+ Validates that an active user exists with the given email address.
+ """
+ UserModel = get_user_model()
+ email = self.cleaned_data["email"]
+ self.users_cache = UserModel._default_manager.filter(email__iexact=email)
+ if not len(self.users_cache):
+ raise forms.ValidationError(self.error_messages['unknown'])
+ if not any(user.is_active for user in self.users_cache):
+ # none of the filtered users are active
+ raise forms.ValidationError(self.error_messages['unknown'])
+ if any((user.password == UNUSABLE_PASSWORD)
+ for user in self.users_cache):
+ raise forms.ValidationError(self.error_messages['unusable'])
+ return email
+
+ def save(self, domain_override=None,
+ subject_template_name='registration/password_reset_subject.txt',
+ email_template_name='registration/password_reset_email.html',
+ use_https=False, token_generator=default_token_generator,
+ from_email=None, request=None):
+ """
+ Generates a one-use only link for resetting password and sends to the
+ user.
+ """
+ from django.core.mail import send_mail
+ for user in self.users_cache:
+ if not domain_override:
+ current_site = get_current_site(request)
+ site_name = current_site.name
+ domain = current_site.domain
+ else:
+ site_name = domain = domain_override
+ c = {
+ 'email': user.email,
+ 'domain': domain,
+ 'site_name': site_name,
+ 'uid': int_to_base36(user.pk),
+ 'user': user,
+ 'token': token_generator.make_token(user),
+ 'protocol': use_https and 'https' or 'http',
+ }
+ subject = loader.render_to_string(subject_template_name, c)
+ # Email subject *must not* contain newlines
+ subject = ''.join(subject.splitlines())
+ email = loader.render_to_string(email_template_name, c)
+ send_mail(subject, email, from_email, [user.email])
+
+
+class SetPasswordForm(forms.Form):
+ """
+ A form that lets a user change set his/her password without entering the
+ old password
+ """
+ error_messages = {
+ 'password_mismatch': _("The two password fields didn't match."),
+ }
+ new_password1 = forms.CharField(label=_("New password"),
+ widget=forms.PasswordInput)
+ new_password2 = forms.CharField(label=_("New password confirmation"),
+ widget=forms.PasswordInput)
+
+ def __init__(self, user, *args, **kwargs):
+ self.user = user
+ super(SetPasswordForm, self).__init__(*args, **kwargs)
+
+ def clean_new_password2(self):
+ password1 = self.cleaned_data.get('new_password1')
+ password2 = self.cleaned_data.get('new_password2')
+ if password1 and password2:
+ if password1 != password2:
+ raise forms.ValidationError(
+ self.error_messages['password_mismatch'])
+ return password2
+
+ def save(self, commit=True):
+ self.user.set_password(self.cleaned_data['new_password1'])
+ if commit:
+ self.user.save()
+ return self.user
+++ /dev/null
-import os.path, re
-import json
-
-from random import choice
-
-from django.core.mail import send_mail
-
-from django.views.generic import View
-from django.shortcuts import render
-
-from unfold.loginrequired import FreeAccessView
-from ui.topmenu import topmenu_items_live
-
-from manifold.manifoldapi import execute_admin_query
-from manifold.core.query import Query
-
-from portal.forms import PassResetForm
-from portal.actions import manifold_update_user
-
-
-class PassResetView (FreeAccessView):
- def post (self, request):
- form = PassResetForm(request.POST) # A form bound to the POST data
- if form.is_valid(): # All validation rules pass
- # Process the data in form.cleaned_data
- email = form.cleaned_data['email'] # email of the user
-
- sender = 'support@myslice.info'
- #recipients = authority_get_pi_emails(authority_hrn)
- recipients = [email ]
- pass_list = ['demo', 'test', 'abcdef']
- reset_pass = choice (pass_list)
- msg = "Your password has been reset to: %s"% reset_pass + "\n\n Please use this temporary password to login and reset your password from MyAccount page."
- print "test ", msg
- send_mail("Onelab Portal: Password reset request", msg, sender, recipients)
- return render(request,'pass_reset_sent.html') # Redirect after POST
- else:
- return self._display (request, form)
-
- def get (self, request):
- return self._display (request, PassResetForm()) # A fresh unbound form
-
- def _display (self, request, form):
- return render(request, 'pass_reset.html', {
- 'form': form,
- })
-
import json
from django.core.mail import send_mail
-
+from django.contrib.auth.models import User
from django.views.generic import View
from django.template.loader import render_to_string
from django.shortcuts import render
#keypair = re.sub("\n", "\\n", keypair)
#keypair = keypair.rstrip('\r\n')
#keypair = ''.join(keypair.split())
+ #for sending email: removing existing double qoute
+ public_key = public_key.replace('"', '');
else:
up_file = request.FILES['user_public_key']
file_content = up_file.read()
auth_type = 'user'
# for sending email
public_key = file_content
+ public_key = ''.join(public_key.split())
else:
errors.append('Please upload a valid RSA public key [.txt or .pub].')
keypair = keypair,
)
b.save()
+ # saves the user to django auth_user table [needed for password reset]
+ user = User.objects.create_user(reg_fname, reg_email, request.POST['password'])
#creating user to manifold local:user
config = '{"firstname":"'+ reg_fname + '", "lastname":"'+ reg_lname + '", "authority":"'+ reg_auth + '"}'
user_params = {'email': reg_email, 'password': request.POST['password'], 'config': config}
'authority_hrn' : reg_auth,
'email' : reg_email,
'user_hrn' : user_hrn,
- 'keypair' : 'Public Key :' + public_key,
+ 'keypair' : 'Public Key: ' + public_key,
'cc_myself' : True # form.cleaned_data['cc_myself']
}
recipients = authority_get_pi_emails(request,reg_auth)
+++ /dev/null
-{% extends "layout-unfold1.html" %}
-
-{% block head %}
-{{ wizard.form.media }}
-{% endblock %}
-
-{% block unfold_main %}
-
-<link rel="stylesheet" type="text/css" href="{{STATIC_URL}}/css/onelab.css" />
-<div class="onelab-title well-lg">
- <h2>Onelab Password Reset</h2>
-</div>
-
-<!-- no hint for this form, moreover we need more space to write stuff down
- so we use a 3-6-3 layout -->
-<div class='well'>
- <form class="cmxform form-horizontal" id="commentForm" action="#" method="post" role="form">{% csrf_token %}
- <fieldset>
- {% for field in form %}
- <div class="form-group">
- <label for="{{ field.html_name }}" class="col-xs-3 control-label">{{ field.label }}</label>
- <div class="col-xs-6"> {{ field.errors }} {{ field }} <p class="form-hint">{{ field.help_text }}</p> </div>
- </div>
- {% endfor %}
- <button class="submit btn btn-default col-xs-12" type="submit">Reset Password</button>
- </fieldset>
- </form>
-</div>
-{% endblock %}
-
+++ /dev/null
-{% extends "layout-unfold1.html" %}
-
-{% block unfold_main %}
-
-<h1>Under Construction.</h1>
-<h3>Please <a href="/portal/contact/">Contact Support</a> for password recovery.</h2>
-
-{% endblock %}
-
# hopefully these should move in dedicated source files too
from portal.views import PresViewView, pres_view_static, pres_view_methods, pres_view_animation
from portal.views import ValidatePendingView
-
+from portal.django_passresetview import password_reset, password_reset_done, password_reset_confirm, password_reset_complete
# DEPRECATED #named_register_forms = (
# DEPRECATED # ("step1", RegisterUserForm),
url(r'^account/account_process/?$', account_process),
url(r'^register/?$', RegistrationView.as_view(), name='registration'),
url(r'^contact/?$', ContactView.as_view(), name='contact'),
- url(r'^pass_reset/?$', PassResetView.as_view(), name='pass_rest'),
+ #url(r'^pass_reset/?$', PassResetView.as_view(), name='pass_rest'),
# Slice request
url(r'^slice_request/?$', SliceRequestView.as_view(), name='slice_request'),
# Validate pending requests
#url(r'^slice/request/?$', views.slice_request, name='slice_request'),
# Slice confirmation
#url(r'^slice/validate/?$', views.slice_validate, name='slice_validate'),
+ url(r'^pass_reset/$',
+ 'portal.django_passresetview.password_reset',
+ {'post_reset_redirect' : '/portal/password/reset/done/'}),
+ (r'^password/reset/done/$',
+ 'portal.django_passresetview.password_reset_done'),
+ (r'^password/reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
+ 'portal.django_passresetview.password_reset_confirm',
+ {'post_reset_redirect' : '/portal/password/done/'}),
+ (r'^password/done/$',
+ 'portal.django_passresetview.password_reset_complete'),
+ # ...
+
)
# (r'^accounts/', include('registration.backends.default.urls')),
# DEPRECATED # url(r'^$', views.index, name='index'),
# DEPRECATED # url(r"^registerwizard/(?P<step>[-\w]+)/$", register_wizard,
# DEPRECATED # name="register_wizard_step"),
-# DEPRECATED # url(r"^registerwizard/$", register_wizard, name="register_wizard")
+# DEPRECATED # url(r"^registerwizard/$", regster_wizard, name="register_wizard")
--- /dev/null
+{% extends "layout-unfold1.html" %}
+{% load i18n %}
+
+{% block unfold_main %}
+
+
+{% block content %}
+
+<h2>{% trans 'OneLab secured Password reset wizard' %}</h2>
+<h3>{% trans 'Password reset successful' %}</h3>
+
+<p>{% trans "Your password has been set. You may go ahead and log in now." %}</p>
+
+<p><a href="{{ login_url }}">{% trans 'Log in' %}</a></p>
+
+{% endblock %}
+{% endblock %}
+
--- /dev/null
+{% extends "layout-unfold1.html" %}
+{% load i18n %}
+
+{% block unfold_main %}
+
+
+{% block content %}
+
+{% if validlink %}
+
+<h2>{% trans 'OneLab secured Password reset wizard' %}</h2>
+<h3>{% trans 'Enter new password' %}</h3>
+
+<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
+
+<form action="" method="post">{% csrf_token %}
+{{ form.new_password1.errors }}
+<p class="aligned wide"><label for="id_new_password1">{% trans 'New password:' %}</label>{{ form.new_password1 }}</p>
+{{ form.new_password2.errors }}
+<p class="aligned wide"><label for="id_new_password2">{% trans 'Confirm password:' %}</label>{{ form.new_password2 }}</p>
+<p><input type="submit" value="{% trans 'Change my password' %}" /></p>
+</form>
+
+{% else %}
+
+<h2>{% trans 'OneLab secured Password reset wizard' %}</h2>
+<h3>{% trans 'Password reset unsuccessful!' %}</h3>
+
+<p> The password reset link was invalid, possibly because it has already been used. Please request a new <a href="/portal/pass_reset/">password reset.</a></p>
+
+<p>If you still encounter problem please <a href="/portal/contact/">Contact Support</a> for password recovery.</p>
+{% endif %}
+
+{% endblock %}
+{% endblock %}
+
--- /dev/null
+{% extends "layout-unfold1.html" %}
+{% load i18n %}
+
+{% block unfold_main %}
+
+{% block content %}
+
+<h2>{% trans 'Onelab secured Password reset wizard' %}</h2>
+
+<p>{% trans "We've emailed you instructions for setting your password to the email address you submitted. You should be receiving it shortly." %}</p>
+
+{% endblock %}
+{% endblock %}
+
--- /dev/null
+{% load i18n %}{% autoescape off %}
+{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktrans %}
+
+{% trans "Please go to the following page and choose a new password:" %}
+{% block reset_link %}
+{{ protocol }}://{{ domain }}{% url 'portal.django_passresetview.password_reset_confirm' uidb36=uid token=token %}
+{% endblock %}
+{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
+
+{% trans "Thanks for using our site!" %}
+
+{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
+
+{% endautoescape %}
--- /dev/null
+{% extends "layout-unfold1.html" %}
+{% load i18n %}
+
+{% block unfold_main %}
+
+
+
+{% block content %}
+
+<h2>{% trans "Welcome to Onelab secured Password reset wizard" %}</h2>
+
+<p>{% trans "Forgotten your password? Enter your email address below, and we'll email instructions for setting a new one." %}</p>
+
+<form action="" method="post">{% csrf_token %}
+{{ form.email.errors }}
+<p><label for="id_email">{% trans 'Email address:' %}</label> {{ form.email }} <input type="submit" value="{% trans 'Reset my password' %}" /></p>
+</form>
+
+{% endblock %}
+{% endblock %}
+