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

Merge branches 'master' and 'master' of source.puri.sm:liberty/ldh_middleware

parents 215b019a 6290d67a
......@@ -22,8 +22,8 @@
<input type="hidden" name="next" value="{{ next }}"/>
</div>
</form>
<p>New user?<a href="{{ site_settings.SUBSCRIBE_HREF }}">Subscribe now!</a></p>
<p><a href="{% url 'password_reset_recover' %}"> forgot your password? </a></p>
<p>new user? <a href="{{ site_settings.SUBSCRIBE_HREF }}">register now!</a></p>
<p><a href="{% url 'password_reset_recover' %}">forgot your password?</a></p>
</section>
</section>
{% endblock %}
......@@ -26,13 +26,13 @@ SPDX-License-Identifier: AGPL-3.0
<a href="{% url 'home' %}"><img class="logo" src="{% static 'logo.png' %}" alt="{{ site_title }}"/></a>
<div id="title_text">
<h1>{% trans "User profile" %}</h1>
{% if request.user.chosenreward.is_pending %}
{% if haschosenreward and request.user.chosenreward.is_pending %}
<div class="notice">
<p>
Your registration is pending.
</p>
<p>
Complete the registration process please visit <a href="{{complete_signup_url}}">{{complete_signup_url}}</a>
To complete the registration process please visit <a href="{{complete_signup_url}}">{{complete_signup_url}}</a>
</p>
<p>
Pending registrations are removed after 12 hours.
......@@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0
<hr/>
{% if not request.user.chosenreward.is_pending %}
{% if (haschosenreward and not request.user.chosenreward.is_pending) or not haschosenreward %}
<main class="col-wrapper form">
<article class="col-1">
......@@ -131,7 +131,7 @@ SPDX-License-Identifier: AGPL-3.0
<h2>{% trans "Group invites" %}</h2>
<ul>
{% if not invitations['links_not_used'] %}
<li>{% trans "None left" %}</li>
<li>{% trans "No pending links" %}</li>
{% else %}
{% for invitation in invitations['list'] %}
{% if not invitation['expired'] and not invitation['consumed'] %}
......@@ -151,13 +151,12 @@ SPDX-License-Identifier: AGPL-3.0
{% endif %}
{% if DEBUG_CHANGE_PASSWORD %}
<h2>{% trans "Profile management" %}</h2>
<ul>
<li><a href="{% url 'auth_password_change' %}">{% trans "Change password" %}</a></li>
<li><a href="{% url 'password_change' %}">{% trans "Change password" %}</a></li>
<li><a href="{% url 'profile_configure' username %}">{% trans "Profile settings" %}</a></li>
</ul>
{% endif %}
</article>
<nav class="col-2">
......
......@@ -65,6 +65,7 @@ def userlimit(request):
"link_profile_ordered_dict": settings.LINK_PROFILE_ORDERED_DICT,
"action_function": action_function,
"invitations": invitations,
"haschosenreward": hasattr(request.user, 'chosenreward'),
"complete_signup_url": CartRegistrationView().get_success_url(request.user),
}
......
......@@ -24,7 +24,8 @@ from ldapregister.forms import RegistrationForm
from ldapregister.views import LdhLoginView
from cart.views import CartRegistrationView
from purist.views import Recovery
from purist.views import Recovery, PasswordChange, \
PasswordChangeDone, ProfileConfigureView
from invitation.views import InvitationRegistrationView
#
......@@ -48,9 +49,15 @@ urlpatterns = [
limitmonitor.views.toggle_tunnel, name='toggle_tunnel'),
url(r'^accounts/profile/new_invitation',
limitmonitor.views.new_invitation, name='new_invitation'),
url(r'^accounts/profile/configure/(?P<username>.+)/$',
ProfileConfigureView.as_view(), name='profile_configure'),
# 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/password_change/$', PasswordChange.as_view(),
name='password_change'),
url(r'^accounts/password_change_done/$', PasswordChangeDone.as_view(),
name='password_change_done'),
url(r'^accounts/', include('password_reset.urls')),
url(r'^accounts/', include('registration.backends.simple.urls')),
url(r'^download/', include('django_agpl.urls')),
......
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.forms import PasswordChangeForm \
as BasePasswordChangeForm
from django.contrib.auth import authenticate
from password_reset.forms import PasswordRecoveryForm as BasePasswordRecoveryForm
from .models import User
class PasswordRecoveryForm(BasePasswordRecoveryForm):
......@@ -10,3 +14,48 @@ class PasswordRecoveryForm(BasePasswordRecoveryForm):
self.fields['username_or_email'] = forms.CharField(
label = _('Enter your recovery email')
)
class PasswordChangeForm(BasePasswordChangeForm):
# Override clean_old_password() to use authenticate which uses
# LDAP authentication
def clean_old_password(self):
old_password = self.cleaned_data["old_password"]
if not authenticate(
request=None,
username=self.user.get_username(),
password=old_password
):
raise forms.ValidationError(
self.error_messages['password_incorrect'],
code='password_incorrect',
)
return old_password
# Override save() to change password in LDAP and WooCommerce. We
# don't really use commit as the new password is always stored.
def save(self, commit=True):
password = self.cleaned_data["new_password1"]
self.user.set_ldap_password(password)
self.user.set_woocommerce_password(raw_password=password,
woocommerce_id=None)
return self.user
class ProfileConfigureForm(forms.ModelForm):
class Meta:
model = User
fields = ['email', 'billing_email']
labels = {
'email': _('Recovery email'),
'billing_email': _('Billing email'),
}
widgets = {
'email': forms.EmailInput(attrs={'size': '100%'}),
'billing_email': forms.EmailInput(attrs={'size': '100%'}),
}
def save(self, commit=True):
self.instance.set_woocommerce_billing_email()
return super(ProfileConfigureForm, self).save(commit)
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2019-04-25 07:23
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('purist', '0004_add_account_type_field'),
]
operations = [
migrations.AddField(
model_name='user',
name='billing_email',
field=models.EmailField(default='', max_length=254),
),
]
......@@ -85,6 +85,7 @@ class User(AbstractUser):
tunnel_password = models.CharField(max_length=250, default=None, null=True)
account_type = EnumIntegerField(enum=AccountType,
default=AccountType.UNDEFINED)
billing_email = models.EmailField(default='', null=False)
@classmethod
def normalize_username(cls, username):
......@@ -165,7 +166,7 @@ class User(AbstractUser):
def get_woocommerce_id(self):
query = "customers?email=" + self.get_identity()
query = "customers?role=all&email=" + self.get_identity()
json = self.woo_get_json(query)
if json is not None and len(json) == 1:
......@@ -218,6 +219,32 @@ class User(AbstractUser):
def get_identity(self):
return self.get_username() + "@" + settings.SITE_DOMAIN.lower()
def get_woocommerce_billing_email(self):
query = "customers?role=all&email=" + self.get_identity()
json = self.woo_get_json(query)
if json is not None and len(json) == 1:
billing_email = json[0]["billing"]["email"]
if billing_email != '':
return billing_email
return None
def set_woocommerce_billing_email(self, woocommerce_id=None):
data = {
"billing": {
"email": self.billing_email
},
}
if woocommerce_id is None:
woocommerce_id = self.get_woocommerce_id()
query = "customers/" + str(woocommerce_id)
return self.woo_put_json(query, data)
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
# save django user
......
<!DOCTYPE html>
{% load static %}
{% load i18n %}
<!--
Keel (LDH middleware)
Copyright 2017-2018 Purism SPC
https://source.puri.sm/liberty/ldh_middleware
SPDX-License-Identifier: AGPL-3.0
-->
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'layout.css' %}"/>
<link rel="stylesheet" href="{% static 'theme.css' %}"/>
<title>{% trans "Change Password" %}</title>
<link rel="icon" sizes="960x960" href="{% static 'favicon.png' %}"/>
<meta name="application-name" content="{{ site_title }}"/>
<meta charset="UTF-8"/>
</head>
<body>
<header>
<div id="title_box">
<a href="{% url 'home' %}"><img class="logo" src="{% static 'logo.png' %}" alt="{{ site_title }}"/></a>
<div id="title_text">
<h1>{% trans "Change Password" %}</h1>
</div>
</div>
<div id="log_state">
{% if request.user.is_authenticated %}
{% trans "Logged in as" %} {{ username }}<br/>
<a href="{% url 'profile' %}">{% trans "Profile" %}</a> |
{% if request.user.is_superuser %}
<a href="{% url 'admin:index' %}">{% trans "Admin" %}</a> |
{% endif %}
<a href="{% url 'auth_logout' %}">{% trans "Log out" %}</a>
{% else %}
{% trans "You are not logged in." %}<br/>
<a href="{% url 'auth_login' %}">{% trans "Log in" %}</a>
<!--
{% trans "or" %}
<a href="{% url 'registration_register' %}">{% trans "register." %}</a>
-->
{% endif %}
</div>
</header>
<hr/>
<main class="col-wrapper form">
<article class="col-1">
<form method="post">{% csrf_token %}
<div>
{% if form.errors %}
<p class="errornote">
{% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}
</p>
{% endif %}
<p>{% trans "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." %}</p>
<fieldset>
<div >
{{ form.old_password.errors }}
{{ form.old_password.label_tag }} {{ form.old_password }}
</div>
<div >
{{ form.new_password1.errors }}
{{ form.new_password1.label_tag }} {{ form.new_password1 }}
{% if form.new_password1.help_text %}
<div class="help">{{ form.new_password1.help_text|safe }}</div>
{% endif %}
</div>
<div >
{{ form.new_password2.errors }}
{{ form.new_password2.label_tag }} {{ form.new_password2 }}
{% if form.new_password2.help_text %}
<div class="help">{{ form.new_password2.help_text|safe }}</div>
{% endif %}
</div>
</fieldset>
<div >
<input type="submit" value="{% trans 'Change my password' %}" class="default" />
</div>
</div>
</form>
</main>
<footer>
<div id="footer_block">
<p>
<em>{{ site_title }}</em> provided by <a href="{{ site_provider_link }}">{{ site_provider }}</a><br />
Code shared under AGPL-3.0-or-later
(<a href="https://source.puri.sm/liberty/ldh_middleware">project</a>,
<a href="{% url 'download-zip' %}">source</a>,
<a href="{% url 'jslicense' %}" rel="jslicense">javascript</a>)
</p>
</div>
</footer>
</body>
</html>
<!DOCTYPE html>
{% load static %}
{% load i18n %}
<!--
Keel (LDH middleware)
Copyright 2017-2018 Purism SPC
https://source.puri.sm/liberty/ldh_middleware
SPDX-License-Identifier: AGPL-3.0
-->
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'layout.css' %}"/>
<link rel="stylesheet" href="{% static 'theme.css' %}"/>
<title>{% trans "Change Password" %}</title>
<link rel="icon" sizes="960x960" href="{% static 'favicon.png' %}"/>
<meta name="application-name" content="{{ site_title }}"/>
<meta charset="UTF-8"/>
</head>
<body>
<header>
<div id="title_box">
<a href="{% url 'home' %}"><img class="logo" src="{% static 'logo.png' %}" alt="{{ site_title }}"/></a>
<div id="title_text">
<h1>{% trans "Change Password" %}</h1>
</div>
</div>
<div id="log_state">
{% if request.user.is_authenticated %}
{% trans "Logged in as" %} {{ username }}<br/>
<a href="{% url 'profile' %}">{% trans "Profile" %}</a> |
{% if request.user.is_superuser %}
<a href="{% url 'admin:index' %}">{% trans "Admin" %}</a> |
{% endif %}
<a href="{% url 'auth_logout' %}">{% trans "Log out" %}</a>
{% else %}
{% trans "You are not logged in." %}<br/>
<a href="{% url 'auth_login' %}">{% trans "Log in" %}</a>
<!--
{% trans "or" %}
<a href="{% url 'registration_register' %}">{% trans "register." %}</a>
-->
{% endif %}
</div>
</header>
<hr/>
<main class="col-wrapper form">
<article class="col-1">
<p>{% trans 'Your password was changed successfully.' %}</p>
<a href="{% url 'profile' %}">{% trans "Back to profile" %}</a>
</main>
<footer>
<div id="footer_block">
<p>
<em>{{ site_title }}</em> provided by <a href="{{ site_provider_link }}">{{ site_provider }}</a><br />
Code shared under AGPL-3.0-or-later
(<a href="https://source.puri.sm/liberty/ldh_middleware">project</a>,
<a href="{% url 'download-zip' %}">source</a>,
<a href="{% url 'jslicense' %}" rel="jslicense">javascript</a>)
</p>
</div>
</footer>
</body>
</html>
<!DOCTYPE html>
{% load static %}
{% load i18n %}
<!--
Keel (LDH middleware)
Copyright 2017-2018 Purism SPC
https://source.puri.sm/liberty/ldh_middleware
SPDX-License-Identifier: AGPL-3.0
-->
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'layout.css' %}"/>
<link rel="stylesheet" href="{% static 'theme.css' %}"/>
<title>{% trans "Profile Settings" %}</title>
<link rel="icon" sizes="960x960" href="{% static 'favicon.png' %}"/>
<meta name="application-name" content="{{ site_title }}"/>
<meta charset="UTF-8"/>
</head>
<body>
<header>
<div id="title_box">
<a href="{% url 'home' %}"><img class="logo" src="{% static 'logo.png' %}" alt="{{ site_title }}"/></a>
<div id="title_text">
<h1>{% trans "Profile Settings" %}</h1>
</div>
</div>
<div id="log_state">
{% if request.user.is_authenticated %}
{% trans "Logged in as" %} {{ username }}<br/>
<a href="{% url 'profile' %}">{% trans "Profile" %}</a> |
{% if request.user.is_superuser %}
<a href="{% url 'admin:index' %}">{% trans "Admin" %}</a> |
{% endif %}
<a href="{% url 'auth_logout' %}">{% trans "Log out" %}</a>
{% else %}
{% trans "You are not logged in." %}<br/>
<a href="{% url 'auth_login' %}">{% trans "Log in" %}</a>
<!--
{% trans "or" %}
<a href="{% url 'registration_register' %}">{% trans "register." %}</a>
-->
{% endif %}
</div>
</header>
<hr/>
<main class="col-wrapper form">
<article class="col-1">
<form action="" method="post">{% csrf_token %}
{{ form.as_ul }}
<input type="submit" value={% trans "Save" %}>
</form>
<a href="{% url 'profile' %}">{% trans "Back to profile" %}</a>
</main>
<footer>
<div id="footer_block">
<p>
<em>{{ site_title }}</em> provided by <a href="{{ site_provider_link }}">{{ site_provider }}</a><br />
Code shared under AGPL-3.0-or-later
(<a href="https://source.puri.sm/liberty/ldh_middleware">project</a>,
<a href="{% url 'download-zip' %}">source</a>,
<a href="{% url 'jslicense' %}" rel="jslicense">javascript</a>)
</p>
</div>
</footer>
</body>
</html>
......@@ -8,7 +8,14 @@ from django.http import Http404, FileResponse
from password_reset.views import Recover
from .serializers import UserSerializer
from .forms import PasswordRecoveryForm
from .forms import PasswordRecoveryForm, PasswordChangeForm, \
ProfileConfigureForm
from django.contrib.auth.views import PasswordChangeView \
as BasePasswordChangeView
from django.contrib.auth.views import PasswordChangeDoneView \
as BasePasswordChangeDoneView
from django.urls import reverse_lazy
from django.views.generic.edit import UpdateView
class UserDetail(APIView):
......@@ -32,6 +39,56 @@ class Recovery(Recover):
form_class = PasswordRecoveryForm
class PasswordChangeDone(BasePasswordChangeDoneView):
template_name = 'purist/change_password_done.html'
def get_context_data(self, **kwargs):
context = super(PasswordChangeDone, self).get_context_data(**kwargs)
context['username'] = self.request.user.get_username()
context['site_title'] = settings.SITE_TITLE
context['site_provider'] = settings.SITE_PROVIDER
if self.extra_context is not None:
context.update(self.extra_context)
return context
class PasswordChange(BasePasswordChangeView):
template_name = 'purist/change_password.html'
form_class = PasswordChangeForm
success_url = reverse_lazy('password_change_done')
def get_context_data(self, **kwargs):
context = super(PasswordChange, self).get_context_data(**kwargs)
context['username'] = self.request.user.get_username()
context['site_title'] = settings.SITE_TITLE
context['site_provider'] = settings.SITE_PROVIDER
if self.extra_context is not None:
context.update(self.extra_context)
return context
class ProfileConfigureView(UpdateView):
template_name = 'purist/profile_configure.html'
form_class = ProfileConfigureForm
success_url = reverse_lazy('profile')
model = User
slug_field = 'username'
slug_url_kwarg = 'username'
def get_context_data(self, **kwargs):
context = super(ProfileConfigureView, self).get_context_data(**kwargs)
context['username'] = self.request.user.get_username()
context['site_title'] = settings.SITE_TITLE
context['site_provider'] = settings.SITE_PROVIDER
return context
def get_initial(self):
# Prepopulate form fields with values from User Model and
# WooCommerce
return {"email": self.object.email,
"billing_email": self.object.get_woocommerce_billing_email()}
def home(request):
render_data = {
"username": request.user.get_username(),
......
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