Commit e20543ba authored by Noe Nieto's avatar Noe Nieto 💬

The complete account recovery feature for Purism/LibremOne/task#20

parent 53ac5cb5
Pipeline #5615 canceled with stage
......@@ -26,6 +26,7 @@ Jinja2 = "==2.10"
WooCommerce = "==1.2.1"
django-simple-captcha = "==0.5.10"
djangorestframework = "*"
django-password-reset = "*"
[dev-packages]
# self:
......
# Django
from django.conf import settings
from django.contrib.auth import logout
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django import forms
from django.contrib.auth import get_user_model
# 3rd party
from captcha.fields import CaptchaField
from cart.models import ChosenReward
from ldapregister.forms import RegistrationForm
from registration.backends.simple.views import RegistrationView
from registration.forms import validators
# Others
from cart.models import ChosenReward
from purist.models import AccountType
User = get_user_model()
class CartRegistrationForm(RegistrationForm):
class Meta(RegistrationForm.Meta):
fields = (
User.USERNAME_FIELD,
'password1',
'password2',
'email'
)
field_order = ('username', 'email', 'password1', 'password2', 'captcha')
captcha = CaptchaField(
label=_('Please solve this sum'),
help_text=_('Prove you are not a robot. Solve this addition, subtraction or multiplication problem.')
help_text=_('Prove you are not a robot. Solve this addition, subtraction or multiplication problem.'),
)
email = forms.EmailField(
label=_('Recovery email address'),
help_text=_('Enter an email address were we can send you recovery information.'),
validators=[
validators.validate_confusables_email,
],
required=True,
)
......
......@@ -17,7 +17,9 @@
<input type="hidden" name="next" value="{{ next }}"/>
</div>
</form>
<a href="{% url 'password_reset_recover' %}"> forgot your password? </a>
</section>
</section>
{% endblock %}
......@@ -54,7 +54,8 @@ INSTALLED_APPS += ["crispy_forms",
"captcha",
"cart",
"registration",
"rest_framework"]
"rest_framework",
"password_reset"]
#
# AGPL APPLICATION
......@@ -146,7 +147,7 @@ SITE_BYLINE = config("SITE_BYLINE")
SITE_DOMAIN = config("SITE_DOMAIN")
SITE_PROVIDER = config("SITE_PROVIDER")
SITE_PROVIDER_LINK = config("SITE_PROVIDER_LINK")
EMAIL_DOMAIN = config("EMAIL_DOMAIN")
#
# WOOCOMMERCE
#
......
......@@ -24,7 +24,7 @@ from ldapregister.forms import RegistrationForm
from ldapregister.views import LdhLoginView
from cart.views import CartRegistrationView
from purist.views import Recovery
#
# Set admin titles for this site
#
......@@ -42,10 +42,12 @@ urlpatterns = [
url(r'^accounts/$', RedirectView.as_view(url='/')),
url(r'^accounts/profile/$', limitmonitor.views.userlimit, name='profile'),
url(r'^accounts/profile/purist.ovpn', limitmonitor.views.ovpn_userfile, name='ovpn_userfile'),
url(r'^accounts/profile/toggle_tunnel',
limitmonitor.views.toggle_tunnel, name='toggle_tunnel'),
url(r'^accounts/profile/toggle_tunnel',
limitmonitor.views.toggle_tunnel, name='toggle_tunnel'),
# url(r'^accounts/register/$', RegistrationView.as_view(form_class=RegistrationForm), name='registration_register'),
url(r'^accounts/login/$', LdhLoginView.as_view(), name='auth_login'),
url(r'^accounts/recover/$', Recovery.as_view(), name='password_reset_recover'),
url(r'^accounts/', include('password_reset.urls')),
url(r'^accounts/', include('registration.backends.simple.urls')),
url(r'^download/', include('django_agpl.urls')),
url(r'^jslicense/$', purist.views.jslicense, name='jslicense'),
......
from django import forms
from django.utils.translation import ugettext_lazy as _
from password_reset.forms import PasswordRecoveryForm as BasePasswordRecoveryForm
class PasswordRecoveryForm(BasePasswordRecoveryForm):
def __init__(self, *args, **kwargs):
super(PasswordRecoveryForm, self).__init__(*args, **kwargs)
self.fields['username_or_email'] = forms.CharField(
label = _('Enter your recovery email')
)
......@@ -365,6 +365,12 @@ class User(AbstractUser):
return (tun_user, tun_passwd)
return None
@property
def recovery_email(self):
if self.account_type is AccountType.COMPLETE:
return self.username + '@' + settings.EMAIL_DOMAIN
return self.email
def encrypt(data):
# Encrypt data with Fernet
......
{% extends "password_reset/base.html" %}
{% load i18n %}
{% block title %}{% trans "Account recovery" %}{% endblock %}
{% block header %}{% trans "Account recovery" %}{% endblock %}
{% block byline %}{% trans "If you previously entered a recovery email, you can use it now. " %}{% endblock %}
{% block login_status%}{% endblock %}
{% block content %}
<form method="post" action="{{ url }}">
{% csrf_token %}
{{ form.as_p }}
<p><input type="submit" value="{% trans "Recover my account" %}"></p>
</form>
{% endblock %}
......@@ -4,8 +4,11 @@ from .models import User
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions
from .serializers import UserSerializer
from django.http import Http404, FileResponse
from password_reset.views import Recover
from .serializers import UserSerializer
from .forms import PasswordRecoveryForm
class UserDetail(APIView):
......@@ -25,6 +28,10 @@ class UserDetail(APIView):
return Response(serializer.data)
class Recovery(Recover):
form_class = PasswordRecoveryForm
def home(request):
render_data = {
"username": request.user.get_username(),
......
# do not modify manually
# generated with "pipenv run pip freeze > requirements.txt"
amqp==2.3.2
amqp==2.4.2
asn1crypto==0.24.0
bcrypt==3.1.4
billiard==3.5.0.4
astroid==2.1.0
bcrypt==3.1.6
billiard==3.5.0.5
bleach==3.1.0
celery==4.1.1
certifi==2018.8.13
cffi==1.11.5
certifi==2019.3.9
cffi==1.12.2
chardet==3.0.4
choicesenum==0.2.2
confusable-homoglyphs==3.0.0
cryptography==2.3.1
cryptography==2.6.1
dj-database-url==0.4.2
Django==1.11.17
Django==1.11.18
django-agpl==4.0.0
django-auth-ldap==1.3.0
django-celery-beat==1.1.0
django-crispy-forms==1.7.0
django-debug-toolbar==1.11
django-extensions==1.9.9
django-ldapdb==0.9.0
django-ipython==0.1.1
django-ldapdb==1.0.0
django-password-reset==2.0
django-ranged-response==0.2.0
django-registration==2.4.1
django-simple-captcha==0.5.10
djangorestframework==3.9.2
docutils==0.14
ephem==3.7.6.0
idna==2.7
idna==2.8
isort==4.3.4
Jinja2==2.10
jinja2-django-tags==0.5
kombu==4.2.1
MarkupSafe==1.0
kombu==4.5.0
lazy-object-proxy==1.3.1
MarkupSafe==1.1.1
mccabe==0.6.1
ordereddict==1.1
paramiko==2.4.1
Pillow==6.0.0
pkg-resources==0.0.0
pkginfo==1.5.0.1
pyasn1==0.4.2
pyasn1-modules==0.2.2
pycparser==2.18
pyasn1-modules==0.2.4
pycparser==2.19
Pygments==2.3.1
pyldap==3.0.0.post1
PyNaCl==1.2.1
python-dateutil==2.7.3
pylint==2.2.2
PyNaCl==1.3.0
python-dateutil==2.8.0
python-decouple==3.1
python-ldap==3.0.0
pytz==2018.5
requests==2.19.1
ruamel.yaml==0.15.55
six==1.11.0
pytz==2018.9
readme-renderer==24.0
requests==2.21.0
requests-toolbelt==0.9.1
ruamel.yaml==0.15.89
six==1.12.0
sqlparse==0.3.0
strictyaml==0.11.10
typing==3.6.4
urllib3==1.23
vine==1.1.4
tqdm==4.31.1
twine==1.12.1
typing==3.6.6
urllib3==1.24.1
vine==1.3.0
webencodings==0.5.1
WooCommerce==1.2.1
wrapt==1.11.1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment