Merge branch 'task/5509/improve-asking-ssh-key' into 'master'

Task/5509/improve asking ssh key

See merge request ungleich-public/dynamicweb!709
This commit is contained in:
pcoder116 2019-07-03 16:56:30 +02:00
commit 20a5e51cad
15 changed files with 204 additions and 194 deletions

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-26 20:44+0000\n"
"POT-Creation-Date: 2019-07-03 11:18+0000\n"
"PO-Revision-Date: 2018-03-30 23:22+0000\n"
"Last-Translator: b'Anonymous User <coder.purple+25@gmail.com>'\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -26,6 +26,22 @@ msgstr ""
msgid "Your New VM %(vm_name)s at Data Center Light"
msgstr "Deine neue VM %(vm_name)s bei Data Center Light"
msgid "Your VM is almost ready!"
msgstr "Deine VM ist fast fertig!"
msgid ""
"You need to specify your public SSH key to access your VM. You can either "
"add your existing key, or generate a new key pair by clicking the generate "
"button below. After choosing your public SSH key option youll be directed "
"to the order confirmation page."
msgstr ""
"Du musst deinen öffentlichen SSH-Schlüssel angeben, um auf deine VM "
"zugreifen zu können. Du kannst entweder deinen vorhandenen Schlüssel "
"hinzufügen oder ein neues Schlüsselpaar generieren, indem du auf die "
"Schaltfläche \"Generieren\" unten klickst. Nachdem du deine öffentliche SSH-"
"Schlüsseloption ausgewählt hast, wirst du zur Bestellbestätigungsseite "
"weitergeleitet. "
msgid "All Rights Reserved"
msgstr "Alle Rechte vorbehalten"
@ -134,6 +150,10 @@ msgstr "Unser Angebot beginnt bei 15 CHF pro Monat. Probier's jetzt aus!"
msgid "ORDER VM"
msgstr "VM BESTELLEN"
#, python-format
msgid "Please enter a value in range %(min_ram)s - 200."
msgstr "Bitte gib einen Wert von %(min_ram)s bis 200 ein."
msgid "VM hosting"
msgstr ""
@ -152,9 +172,6 @@ msgstr "Standort: Schweiz"
msgid "Please enter a value in range 1 - 48."
msgstr "Bitte gib einen Wert von 1 bis 48 ein."
msgid "Please enter a value in range 1 - 200."
msgstr "Bitte gib einen Wert von 1 bis 200 ein."
msgid "Please enter a value in range 10 - 2000."
msgstr "Bitte gib einen Wert von 10 bis 2000 ein."
@ -413,6 +430,10 @@ msgstr "Zwischensumme"
msgid "VAT"
msgstr "Mehrwertsteuer"
#, fuzzy, python-format
#| msgid ""
#| "By clicking \"Place order\" this plan will charge your credit card "
#| "account with %(total_price)s CHF/month"
msgid ""
"By clicking \"Place order\" this plan will charge your credit card account "
"with %(total_price)s CHF/month"
@ -420,6 +441,10 @@ msgstr ""
"Wenn Du \"bestellen\" auswählst, wird Deine Kreditkarte mit "
"%(vm_total_price)s CHF pro Monat belastet"
#, fuzzy, python-format
#| msgid ""
#| "By clicking \"Place order\" this payment will charge your credit card "
#| "account with a one time amount of %(total_price)s CHF"
msgid ""
"By clicking \"Place order\" this payment will charge your credit card "
"account with a one time amount of %(total_price)s CHF"
@ -535,6 +560,9 @@ msgstr "Tagen sagen mehr als Worte Teste jetzt unsere VM!"
msgid "Invalid number of cores"
msgstr "Ungültige Anzahle CPU-Kerne"
msgid "Invalid calculator properties"
msgstr ""
msgid "Invalid RAM size"
msgstr "Ungültige RAM-Grösse"

View file

@ -0,0 +1,10 @@
{% load staticfiles bootstrap3 i18n custom_tags humanize %}
{% block content %}
{% block userkey_form %}
{% with form_title=_("Your VM is almost ready!") form_sub_title=_("You need to specify your public SSH key to access your VM. You can either add your existing key, or generate a new key pair by clicking the generate button below. After choosing your public SSH key option youll be directed to the order confirmation page.") %}
{% include 'hosting/user_key.html' with title=form_title sub_title=form_sub_title %}
{% endwith %}
{% endblock userkey_form %}
{%endblock%}

View file

@ -134,38 +134,6 @@
</div>
<form id="virtual_machine_create_form" action="" method="POST">
{% csrf_token %}
{% if generic_payment_details %}
{% else %}
{% comment %}
We are in VM buy flow and we want user to click the "Place order" button.
At this point, we also want the user to input the SSH key for the VM.
{% endcomment %}
{% if messages %}
<div class="alert alert-warning">
{% for message in messages %}
<span>{{ message }}</span>
{% endfor %}
</div>
{% endif %}
<div class="dashboard-container-head">
<h2 class="dashboard-title-thin"><i class="fa fa-key" aria-hidden="true"></i>&nbsp;{% trans "Add your public SSH key" %}</h2>
</div>
<div class="existing-keys">
{% if keys|length > 0 %}
<div class="existing-keys-title">Existing keys</div>
{% endif %}
{% for key in keys %}
<textarea class="form-control input-no-border" style="width: 100%" readonly rows="6">
{{key}}
</textarea>
<br/>
{% endfor %}
</div>
{% for field in form %}
{% bootstrap_field field %}
{% endfor %}
{% endif %}
<div class="row">
<div class="col-sm-8">
{% if generic_payment_details %}

View file

@ -1,12 +1,12 @@
from django.conf.urls import url
from django.views.generic import TemplateView, RedirectView
from utils.views import AskSSHKeyView
from .views import (
IndexView, PaymentOrderView, OrderConfirmationView,
WhyDataCenterLightView, ContactUsView
)
urlpatterns = [
url(r'^$', IndexView.as_view(), name='index'),
url(r'^t/$', IndexView.as_view(), name='index_t'),
@ -20,6 +20,8 @@ urlpatterns = [
url(r'^payment/?$', PaymentOrderView.as_view(), name='payment'),
url(r'^order-confirmation/?$', OrderConfirmationView.as_view(),
name='order_confirmation'),
url(r'^add-ssh-key/?$', AskSSHKeyView.as_view(),
name='add_ssh_key'),
url(r'^contact/?$', ContactUsView.as_view(), name='contact_us'),
url(r'glasfaser/?$',
TemplateView.as_view(template_name='ungleich_page/glasfaser.html'),

View file

@ -1,8 +1,9 @@
import logging
import pyotp
import requests
from django.contrib.sites.models import Site
from django.conf import settings
from django.contrib.sites.models import Site
from datacenterlight.tasks import create_vm_task
from hosting.models import HostingOrder, HostingBill, OrderDetail
@ -99,7 +100,8 @@ def clear_all_session_vars(request):
for session_var in ['specs', 'template', 'billing_address',
'billing_address_data', 'card_id',
'token', 'customer', 'generic_payment_type',
'generic_payment_details', 'product_id']:
'generic_payment_details', 'product_id',
'order_confirm_url', 'new_user_hosting_key_id']:
if session_var in request.session:
del request.session[session_var]

View file

@ -17,9 +17,10 @@ from hosting.forms import (
UserHostingKeyForm
)
from hosting.models import (
HostingBill, HostingOrder, UserCardDetail, GenericProduct
HostingBill, HostingOrder, UserCardDetail, GenericProduct, UserHostingKey
)
from membership.models import CustomUser, StripeCustomer
from opennebula_api.models import OpenNebulaManager
from opennebula_api.serializers import VMTemplateSerializer
from utils.forms import (
BillingAddressForm, BillingAddressFormSignup, UserBillingAddressForm,
@ -522,8 +523,16 @@ class PaymentOrderView(FormView):
request.session['customer'] = customer.stripe_id
else:
request.session['customer'] = customer
# For generic payment we take the user directly to confirmation
if ('generic_payment_type' in request.session and
self.request.session['generic_payment_type'] == 'generic'):
return HttpResponseRedirect(
reverse('datacenterlight:order_confirmation'))
else:
self.request.session['order_confirm_url'] = reverse('datacenterlight:order_confirmation')
return HttpResponseRedirect(
reverse('datacenterlight:add_ssh_key'))
else:
context = self.get_context_data()
context['billing_address_form'] = address_form
@ -588,31 +597,6 @@ class OrderConfirmationView(DetailView, FormView):
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
# Check ssh public key and then proceed
form = self.get_form()
required = True
# SSH key validation is required only if the user doesn't have an
# existing key and user has input some value in the add ssh key fields
if (len(get_all_public_keys(self.request.user)) > 0 and
(len(form.data.get('public_key')) == 0 and
len(form.data.get('name')) == 0)):
required = False
form.fields['name'].required = required
form.fields['public_key'].required = required
if not form.is_valid():
response = {
'status': False,
'msg_title': str(_('SSH key related error occurred')),
'msg_body': "<br/>".join([str(v) for k,v in form.errors.items()]),
}
return JsonResponse(response)
if required:
# We have a valid SSH key from the user, save it in opennebula and
# db and proceed further
form.save()
user = request.session.get('user')
stripe_api_cus_id = request.session.get('customer')
stripe_utils = StripeUtils()
@ -864,6 +848,18 @@ class OrderConfirmationView(DetailView, FormView):
new_user = authenticate(username=custom_user.email,
password=password)
login(request, new_user)
if 'new_user_hosting_key_id' in self.request.session:
user_hosting_key = UserHostingKey.objects.get(id=self.request.session['new_user_hosting_key_id'])
user_hosting_key.user = new_user
user_hosting_key.save()
owner = new_user
manager = OpenNebulaManager(
email=owner.email,
password=owner.password
)
keys_to_save = get_all_public_keys(new_user)
manager.save_key_in_opennebula_user('\n'.join(keys_to_save))
else:
# We assume that if the user is here, his/her StripeCustomer
# object already exists

View file

@ -222,7 +222,7 @@ class UserHostingKeyForm(forms.ModelForm):
return self.data.get('name')
def clean_user(self):
return self.request.user
return self.request.user if self.request.user.is_authenticated() else None
def clean(self):
cleaned_data = self.cleaned_data

View file

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2019-07-01 16:14
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('hosting', '0054_auto_20190508_2141'),
]
operations = [
migrations.AlterField(
model_name='userhostingkey',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View file

@ -1,11 +1,11 @@
import json
import logging
import os
import pytz
from datetime import datetime
import pytz
from Crypto.PublicKey import RSA
from dateutil.relativedelta import relativedelta
from datetime import datetime
from django.db import models
from django.utils import timezone
from django.utils.functional import cached_property
@ -187,7 +187,7 @@ class HostingOrder(AssignPermissionsMixin, models.Model):
class UserHostingKey(models.Model):
user = models.ForeignKey(CustomUser)
user = models.ForeignKey(CustomUser, blank=True, null=True)
public_key = models.TextField()
private_key = models.FileField(upload_to='private_keys', blank=True)
created_at = models.DateTimeField(auto_now_add=True)

View file

@ -198,35 +198,6 @@
{% block submit_btn %}
<form method="post" id="virtual_machine_create_form">
{% csrf_token %}
{% comment %}
We are in VM buy flow and we want user to click the "Place order" button.
At this point, we also want the user to input the SSH key for the VM.
{% endcomment %}
{% if messages %}
<div class="alert alert-warning">
{% for message in messages %}
<span>{{ message }}</span>
{% endfor %}
</div>
{% endif %}
<div class="dashboard-container-head">
<h2 class="dashboard-title-thin"><i class="fa fa-key" aria-hidden="true"></i>&nbsp;{% trans "Add your public SSH key" %}</h2>
</div>
<div class="existing-keys">
{% if keys|length > 0 %}
<div class="existing-keys-title">Existing keys</div>
{% endif %}
{% for key in keys %}
<textarea class="form-control input-no-border" style="width: 100%" readonly rows="6">
{{key}}
</textarea>
<br/>
{% endfor %}
</div>
{% for field in form %}
{% bootstrap_field field %}
{% endfor %}
<div class="row">
<div class="col-sm-8">
<div class="dcl-place-order-text">{% blocktrans with vm_price=vm.total_price|floatformat:2|intcomma %}By clicking "Place order" this plan will charge your credit card account with {{ vm_price }} CHF/month{% endblocktrans %}.</div>

View file

@ -8,7 +8,8 @@
<form method="POST" action="" novalidate class="form-ssh">
{% csrf_token %}
<div class="page-header">
<h1 class="h1-thin"><i class="fa fa-key" aria-hidden="true"></i>&nbsp;{% trans "Add your public SSH key" %}</h1>
<h1 class="h1-thin"><i class="fa fa-key" aria-hidden="true"></i>&nbsp;{% if title %}{% trans title %}{% else %} {% endif %}</h1>
{% if sub_title %}<span>{% trans sub_title %}</span>{% else %}{% endif %}
</div>
{% if messages %}
<div class="alert alert-warning">

View file

@ -1,6 +1,7 @@
from django.conf.urls import url
from django.contrib.auth import views as auth_views
from utils.views import SSHKeyCreateView, AskSSHKeyView
from .views import (
DjangoHostingView, RailsHostingView, PaymentVMView, NodeJSHostingView,
LoginView, SignupView, SignupValidateView, SignupValidatedView, IndexView,
@ -8,7 +9,7 @@ from .views import (
VirtualMachinesPlanListView, VirtualMachineView, OrdersHostingDeleteView,
MarkAsReadNotificationView, PasswordResetView, PasswordResetConfirmView,
HostingPricingView, CreateVirtualMachinesView, HostingBillListView,
HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView,
HostingBillDetailView, SSHKeyDeleteView, SSHKeyListView,
SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView,
InvoiceListView, InvoiceDetailView, CheckUserVM
)
@ -27,6 +28,8 @@ urlpatterns = [
url(r'invoices/?$', InvoiceListView.as_view(), name='invoices'),
url(r'order-confirmation/?$', OrdersHostingDetailView.as_view(),
name='order-confirmation'),
url(r'^add-ssh-key/?$', AskSSHKeyView.as_view(),
name='add_ssh_key'),
url(r'orders/(?P<pk>\d+)/?$', OrdersHostingDetailView.as_view(),
name='orders'),
url(r'invoice/(?P<invoice_id>[-\w]+)/?$', InvoiceDetailView.as_view(),

View file

@ -523,67 +523,6 @@ class SSHKeyChoiceView(LoginRequiredMixin, View):
return redirect(reverse_lazy('hosting:ssh_keys'), foo='bar')
@method_decorator(decorators, name='dispatch')
class SSHKeyCreateView(LoginRequiredMixin, FormView):
form_class = UserHostingKeyForm
model = UserHostingKey
template_name = 'hosting/user_key.html'
login_url = reverse_lazy('hosting:login')
context_object_name = "virtual_machine"
success_url = reverse_lazy('hosting:ssh_keys')
def get_form_kwargs(self):
kwargs = super(SSHKeyCreateView, self).get_form_kwargs()
kwargs.update({'request': self.request})
return kwargs
def form_valid(self, form):
form.save()
if settings.DCL_SSH_KEY_NAME_PREFIX in form.instance.name:
content = ContentFile(form.cleaned_data.get('private_key'))
filename = form.cleaned_data.get(
'name') + '_' + str(uuid.uuid4())[:8] + '_private.pem'
form.instance.private_key.save(filename, content)
context = self.get_context_data()
next_url = self.request.session.get(
'next',
reverse('hosting:create_virtual_machine')
)
if 'next' in self.request.session:
context.update({
'next_url': next_url
})
del (self.request.session['next'])
if form.cleaned_data.get('private_key'):
context.update({
'private_key': form.cleaned_data.get('private_key'),
'key_name': form.cleaned_data.get('name'),
'form': UserHostingKeyForm(request=self.request),
})
owner = self.request.user
manager = OpenNebulaManager(
email=owner.email,
password=owner.password
)
keys_to_save = get_all_public_keys(self.request.user)
manager.save_key_in_opennebula_user('\n'.join(keys_to_save))
return HttpResponseRedirect(self.success_url)
def post(self, request, *args, **kwargs):
form = self.get_form()
required = 'add_ssh' in self.request.POST
form.fields['name'].required = required
form.fields['public_key'].required = required
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
@method_decorator(decorators, name='dispatch')
class SettingsView(LoginRequiredMixin, FormView):
template_name = "hosting/settings.html"
@ -830,10 +769,10 @@ class PaymentVMView(LoginRequiredMixin, FormView):
reverse('hosting:payment') + '#payment_error')
request.session['token'] = token
request.session['billing_address_data'] = billing_address_data
return HttpResponseRedirect("{url}?{query_params}".format(
self.request.session['order_confirm_url'] = "{url}?{query_params}".format(
url=reverse('hosting:order-confirmation'),
query_params='page=payment')
)
return HttpResponseRedirect(reverse('hosting:add_ssh_key'))
else:
return self.form_invalid(form)
@ -1002,31 +941,6 @@ class OrdersHostingDetailView(LoginRequiredMixin, DetailView, FormView):
@method_decorator(decorators)
def post(self, request):
# Check ssh public key and then proceed
form = self.get_form()
required = True
# SSH key validation is required only if the user doesn't have an
# existing key and user has input some value in the add ssh key fields
if (len(get_all_public_keys(self.request.user)) > 0 and
(len(form.data.get('public_key')) == 0 and
len(form.data.get('name')) == 0)):
required = False
form.fields['name'].required = required
form.fields['public_key'].required = required
if not form.is_valid():
response = {
'status': False,
'msg_title': str(_('SSH key related error occurred')),
'msg_body': "<br/>".join([str(v) for k,v in form.errors.items()]),
}
return JsonResponse(response)
if required:
# We have a valid SSH key from the user, save it in opennebula and
# db and proceed further
form.save()
template = request.session.get('template')
specs = request.session.get('specs')
stripe_utils = StripeUtils()

View file

@ -376,7 +376,7 @@ class OpenNebulaManager():
"""
return_value = self.oneadmin_client.call(
'user.update',
self.opennebula_user.id,
self.opennebula_user if type(self.opennebula_user) == int else self.opennebula_user.id,
'<CONTEXT><SSH_PUBLIC_KEY>%s</SSH_PUBLIC_KEY></CONTEXT>' % ssh_key,
update_type
)

View file

@ -1,16 +1,25 @@
import uuid
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import authenticate, login
from django.contrib.auth.tokens import default_token_generator
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse_lazy
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView, CreateView
from django.views.decorators.cache import cache_control
from django.views.generic import FormView, CreateView
from datacenterlight.utils import get_cms_integration
from hosting.forms import UserHostingKeyForm
from hosting.models import UserHostingKey
from membership.models import CustomUser
from opennebula_api.models import OpenNebulaManager
from utils.hosting_utils import get_all_public_keys
from .forms import SetPasswordForm
from .mailer import BaseEmail
@ -174,3 +183,87 @@ class PasswordResetConfirmViewMixin(FormView):
form.add_error(None,
_('The reset password link is no longer valid.'))
return self.form_invalid(form)
class SSHKeyCreateView(FormView):
form_class = UserHostingKeyForm
model = UserHostingKey
template_name = 'hosting/user_key.html'
login_url = reverse_lazy('hosting:login')
context_object_name = "virtual_machine"
success_url = reverse_lazy('hosting:ssh_keys')
def get_form_kwargs(self):
kwargs = super(SSHKeyCreateView, self).get_form_kwargs()
kwargs.update({'request': self.request})
return kwargs
def form_valid(self, form):
form.save()
if settings.DCL_SSH_KEY_NAME_PREFIX in form.instance.name:
content = ContentFile(form.cleaned_data.get('private_key'))
filename = form.cleaned_data.get(
'name') + '_' + str(uuid.uuid4())[:8] + '_private.pem'
form.instance.private_key.save(filename, content)
context = self.get_context_data()
next_url = self.request.session.get(
'next',
reverse_lazy('hosting:create_virtual_machine')
)
if 'next' in self.request.session:
context.update({
'next_url': next_url
})
del (self.request.session['next'])
if form.cleaned_data.get('private_key'):
context.update({
'private_key': form.cleaned_data.get('private_key'),
'key_name': form.cleaned_data.get('name'),
'form': UserHostingKeyForm(request=self.request),
})
if self.request.user.is_authenticated():
owner = self.request.user
manager = OpenNebulaManager(
email=owner.email,
password=owner.password
)
keys_to_save = get_all_public_keys(self.request.user)
manager.save_key_in_opennebula_user('\n'.join(keys_to_save))
else:
self.request.session["new_user_hosting_key_id"] = form.instance.id
return HttpResponseRedirect(self.success_url)
def post(self, request, *args, **kwargs):
form = self.get_form()
required = 'add_ssh' in self.request.POST
form.fields['name'].required = required
form.fields['public_key'].required = required
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
class AskSSHKeyView(SSHKeyCreateView):
form_class = UserHostingKeyForm
template_name = "datacenterlight/add_ssh_key.html"
success_url = reverse_lazy('datacenterlight:order_confirmation')
context_object_name = "dcl_vm_buy_add_ssh_key"
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
def get(self, request, *args, **kwargs):
context = {
'site_url': reverse_lazy('datacenterlight:index'),
'cms_integration': get_cms_integration('default'),
'form': UserHostingKeyForm(request=self.request),
'keys': get_all_public_keys(self.request.user)
}
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
self.success_url = self.request.session.get("order_confirm_url")
return super(AskSSHKeyView, self).post(self, request, *args, **kwargs)