Add view to change mailbox password

This commit is contained in:
Santiago L 2021-10-14 11:09:59 +02:00
parent 6c773893f7
commit 33e68b5d07
8 changed files with 130 additions and 33 deletions

View File

@ -24,6 +24,7 @@ API_PATHS = {
'address-detail': 'addresses/{pk}/',
'mailbox-list': 'mailboxes/',
'mailbox-detail': 'mailboxes/{pk}/',
'mailbox-password': 'mailboxes/{pk}/set_password/',
'mailinglist-list': 'lists/',
'saas-list': 'saas/',
'website-list': 'websites/',
@ -194,6 +195,13 @@ class Orchestra(object):
# return self.request("DELETE", url=url, render_as=None)
return self.request("PATCH", url=url, data={"is_active": False})
def set_password_mailbox(self, pk, data):
path = API_PATHS.get('mailbox-password').format_map({'pk': pk})
url = urllib.parse.urljoin(self.base_url, path)
status, response = self.request("POST", url=url, data=data, raise_exception=False)
return status, response
def retrieve_domain(self, pk):
path = API_PATHS.get('domain-detail').format_map({'pk': pk})

View File

@ -60,6 +60,40 @@ class MailForm(forms.Form):
return serialized_data
class MailboxChangePasswordForm(forms.Form):
error_messages = {
'password_mismatch': _('The two password fields didnt match.'),
}
password = forms.CharField(
label=_("Password"),
strip=False,
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
)
password2 = forms.CharField(
label=_("Password confirmation"),
widget=forms.PasswordInput(attrs={'autocomplete': 'new-password'}),
strip=False,
help_text=_("Enter the same password as before, for verification."),
)
def clean_password2(self):
password = self.cleaned_data.get("password")
password2 = self.cleaned_data.get("password2")
if password and password2 and password != password2:
raise ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2
def serialize(self):
assert self.is_valid()
serialized_data = {
"password": self.cleaned_data["password2"],
}
return serialized_data
class MailboxCreateForm(forms.Form):
error_messages = {
'password_mismatch': _('The two password fields didnt match.'),

View File

@ -4,31 +4,30 @@ a, a:hover, a:focus {
}
a:hover {
color: rgba(0,0,0,.7);
color: rgba(0, 0, 0, .7);
}
.btn-arrow-left{
.btn-arrow-left {
color: #eee;
background: #D3D0DA;
position: relative;
padding: 8px 20px 8px 30px;
margin-left: 1em; /** equal value than arrow.left **/
margin-left: 1em;
/** equal value than arrow.left **/
}
.btn-arrow-left::after,
.btn-arrow-left::before{
.btn-arrow-left::after, .btn-arrow-left::before {
content: "";
position: absolute;
top: 50%;
left: -1em;
margin-top: -19px;
border-top: 19px solid transparent;
border-bottom: 19px solid transparent;
border-right: 1em solid;
}
.btn-arrow-left::after{
.btn-arrow-left::after {
border-right-color: #D3D0DA;
z-index: 2;
}
@ -43,13 +42,12 @@ a:hover {
min-width: 280px;
max-width: 280px;
min-height: 100vh;
position: fixed;
z-index: 999;
display: flex;
flex-direction: column;
}
#sidebar #sidebar-services {
flex-grow: 1;
}
@ -62,20 +60,20 @@ a:hover {
padding-left: 2rem;
padding-right: 2rem;
}
#sidebar #sidebar-services {
padding-left: 1rem;
padding-right: 1rem;
}
#sidebar #user-profile-menu {
background:rgba(254, 251, 242, 0.25);
background: rgba(254, 251, 242, 0.25);
}
#sidebar ul.components {
padding: 20px 0;
}
#sidebar ul li a {
padding: 10px;
font-size: 1.1em;
@ -89,25 +87,26 @@ a:hover {
}
.vertical-center {
min-height: 100%; /* Fallback for browsers do NOT support vh unit */
min-height: 100vh; /* These two lines are counted as one :-) */
min-height: 100%;
/* Fallback for browsers do NOT support vh unit */
min-height: 100vh;
/* These two lines are counted as one :-) */
display: flex;
align-items: center;
}
}
/** login **/
#body-login .jumbotron {
background: #282532 no-repeat url("../images/logo-pangea-lilla-bg.svg") right;
}
#login-content {
background:white;
background: white;
padding: 2rem;
}
#login-content input[type="text"].form-control,
#login-content input[type="password"].form-control {
#login-content input[type="text"].form-control, #login-content input[type="password"].form-control {
border-radius: 0;
border: 0;
border-bottom: 2px solid #8E8E8E;
@ -121,6 +120,7 @@ a:hover {
margin-top: 1.5rem;
text-align: center;
}
#login-footer a {
color: #FEFBF2;
}
@ -130,34 +130,37 @@ a:hover {
background-position: right 5% top 10%;
color: #343434;
padding-left: 2rem;
margin-left: 280px; /** sidebar width **/
margin-left: 280px;
/** sidebar width **/
}
/** services **/
h1.service-name {
h1.service-name {
font: Bold 26px/34px Roboto;
margin-top: 3rem;
}
.service-description {
font: 16px/21px Roboto;
}
.table.service-list {
margin-top: 2rem;
table-layout: fixed;
}
/** TODO update theme instead of overriding **/
.service-list thead.thead-dark th,
.service-card .card-header {
.service-list thead.thead-dark th, .service-card .card-header {
background: rgba(80, 70, 110, 0.25);
color: #50466E;
border-color: transparent;
}
/** /TODO **/
.table.service-list td,
.table.service-list th {
.table.service-list td, .table.service-list th {
vertical-align: middle;
}
@ -202,11 +205,10 @@ h1.service-name {
.service-card .card-body {
color: #787878;
}
.service-card .card-body i.fas {
color:#9C9AA7;
color: #9C9AA7;
}
.service-manager-link {
@ -215,8 +217,7 @@ h1.service-name {
right: 15px;
}
.service-card .service-manager-link a,
.service-card .service-manager-link a i.fas {
.service-card .service-manager-link a, .service-card .service-manager-link a i.fas {
color: white;
}
@ -243,11 +244,9 @@ h1.service-name {
font-variant: normal;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
position: absolute;
top: 0;
right: 10px;
color: #E8E7EB;
font-size: 2em;
}
@ -308,3 +307,13 @@ h1.service-name {
border-top: 0;
justify-content: center;
}
.roll-hover {
visibility: hidden;
display: inline-block;
margin-left: 2rem;
}
td:hover .roll-hover {
visibility: visible;
}

View File

@ -0,0 +1,15 @@
{% extends "musician/base.html" %}
{% load bootstrap4 i18n %}
{% block content %}
<h1 class="service-name">{% trans "Change password" %}: <span class="font-weight-light">{{ object.name }}</span></h1>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<a class="btn btn-light mr-2" href="{% url 'musician:mailbox-list' %}">{% trans "Cancel" %}</a>
<button type="submit" class="btn btn-secondary">{% trans "Save" %}</button>
{% endbuttons %}
</form>
{% endblock %}

View File

@ -21,6 +21,7 @@
<button type="submit" class="btn btn-secondary">{% trans "Save" %}</button>
{% if form.instance %}
<div class="float-right">
<a class="btn btn-outline-warning" href="{% url 'musician:mailbox-password' mailbox.id %}"><i class="fas fa-key"></i> {% trans "Change password" %}</a>
<a class="btn btn-danger" href="{% url 'musician:mailbox-delete' view.kwargs.pk %}">{% trans "Delete" %}</a>
</div>
{% endif %}

View File

@ -21,7 +21,11 @@
{# <!-- Exclude (don't render) inactive mailboxes -->#}
{% if mailbox.is_active %}
<tr>
<td><a href="{% url 'musician:mailbox-update' mailbox.id %}">{{ mailbox.name }}</a></td>
<td>
<a href="{% url 'musician:mailbox-update' mailbox.id %}">{{ mailbox.name }}</a>
<a class="roll-hover btn btn-outline-warning" href="{% url 'musician:mailbox-password' mailbox.id %}">
<i class="fas fa-key"></i> {% trans "Update password" %}</a>
</td>
<td>{{ mailbox.filtering }}</td>
<td>
{% for addr in mailbox.addresses %}

View File

@ -27,6 +27,7 @@ urlpatterns = [
path('mailboxes/new/', views.MailboxCreateView.as_view(), name='mailbox-create'),
path('mailboxes/<int:pk>/', views.MailboxUpdateView.as_view(), name='mailbox-update'),
path('mailboxes/<int:pk>/delete/', views.MailboxDeleteView.as_view(), name='mailbox-delete'),
path('mailboxes/<int:pk>/change-password/', views.MailboxChangePasswordView.as_view(), name='mailbox-password'),
path('mailing-lists/', views.MailingListsView.as_view(), name='mailing-lists'),
path('databases/', views.DatabasesView.as_view(), name='database-list'),
path('saas/', views.SaasView.as_view(), name='saas-list'),

View File

@ -20,7 +20,7 @@ from requests.exceptions import HTTPError
from . import api, get_version
from .auth import login as auth_login
from .auth import logout as auth_logout
from .forms import LoginForm, MailboxCreateForm, MailboxUpdateForm, MailForm
from .forms import LoginForm, MailboxChangePasswordForm, MailboxCreateForm, MailboxUpdateForm, MailForm
from .mixins import (CustomContextMixin, ExtendedPaginationMixin,
UserTokenRequiredMixin)
from .models import (Address, Bill, DatabaseService, Mailbox,
@ -435,6 +435,31 @@ class MailboxDeleteView(CustomContextMixin, UserTokenRequiredMixin, DeleteView):
logger.error("Error sending email to managers", exc_info=True)
class MailboxChangePasswordView(CustomContextMixin, UserTokenRequiredMixin, FormView):
template_name = "musician/mailbox_change_password.html"
form_class = MailboxChangePasswordForm
success_url = reverse_lazy("musician:mailbox-list")
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
self.object = self.get_object()
context.update({
'object': self.object,
})
return context
def get_object(self, queryset=None):
obj = self.orchestra.retrieve_mailbox(self.kwargs['pk'])
return obj
def form_valid(self, form):
data = {
'password': form.cleaned_data['password2']
}
status, response = self.orchestra.set_password_mailbox(self.kwargs['pk'], data)
return super().form_valid(form)
class DatabasesView(ServiceListView):
template_name = "musician/databases.html"
service_class = DatabaseService