Commit 95d5bccd by Őry Máté

dashboard: add vm ownership-transfer confirmation

parent d2bbdf9d
{% extends "dashboard/base.html" %}
{% load i18n %}
{% block content %}
<div class="body-content">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="no-margin">
{% trans "Ownership transfer" %}
<div class="panel-body">
{% blocktrans with owner=instance.owner fqdn=instance.primary_host %}
{{ owner }} offered to take the ownership of virtual machine {{fqdn}}.
Do you accept the responsility of being the host's owner?
{% endblocktrans %}
<div class="pull-right">
<form action="" method="POST">
{% csrf_token %}
<a class="btn btn-default" href="{% url "dashboard.index" %}">{% trans "No" %}</a>
<input type="hidden" name="key" value="{{ key }}"/>
<button class="btn btn-danger" type="submit">{% trans "Yes" %}</button>
{% endblock %}
......@@ -3,7 +3,9 @@ from django.conf.urls import patterns, url
from vm.models import Instance
from .views import (
IndexView, VmDetailView, VmList, VmCreate, TemplateDetail, AclUpdateView,
VmDelete, VmMassDelete, vm_activity, NodeList, NodeDetailView)
VmDelete, VmMassDelete, vm_activity, NodeList, NodeDetailView,
urlpatterns = patterns(
......@@ -25,5 +27,7 @@ urlpatterns = patterns(
url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'),
url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(),
url(r'^tx/$', TransferOwnershipConfirmView.as_view(),
......@@ -5,10 +5,10 @@ import re
from django.contrib.auth.models import User, Group
from django.contrib.messages import warning
from django.core.exceptions import PermissionDenied
from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.core import signing
from django.core.urlresolvers import reverse, reverse_lazy
from django.http import HttpResponse, HttpResponseRedirect
from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.shortcuts import redirect, render
from django.views.decorators.http import require_POST
from django.views.generic.detail import SingleObjectMixin
......@@ -17,10 +17,11 @@ from django.contrib import messages
from django.utils.translation import ugettext as _
from django_tables2 import SingleTableView
from braces.views import LoginRequiredMixin
from .tables import (VmListTable, NodeListTable)
from vm.models import (Instance, InstanceTemplate, InterfaceTemplate,
InstanceActivity, Node)
InstanceActivity, Node, instance_activity)
from firewall.models import Vlan
from storage.models import Disk
......@@ -496,3 +497,87 @@ def vm_activity(request, pk):
class TransferOwnershipConfirmView(LoginRequiredMixin, View):
max_age = 3 * 24 * 3600
success_message = _("Ownership successfully transferred.")
def get_salt(cls):
return unicode(cls)
def get(self, request, *args, **kwargs):
"""Confirm ownership transfer based on token.
key = request.GET['key']
logger.debug('Confirm dialog for token %s.', key)
instance, new_owner = self.get_instance(key, request.user)
except KeyError:
raise Http404()
except PermissionDenied():
messages.error(request, _('This token is for an other user.'))
except SuspiciousOperation:
messages.error(request, _('This token is invalid or has expired.'))
raise PermissionDenied()
return render(request,
dictionary={'instance': instance, 'key': key})
def post(self, request, *args, **kwargs):
"""Really transfer ownership based on token.
key = request.POST['key']
instance, owner = self.get_instance(key, request.user)
except KeyError:
logger.debug('Posted to %s without key field.',
raise SuspiciousOperation()
old = instance.owner
with instance_activity(code_suffix='ownership-transferred',
instance=instance, user=request.user):
instance.owner = request.user
messages.success(request, self.success_message)'Ownership of %s transferred from %s to %s.',
unicode(instance), unicode(old), unicode(request.user))
return HttpResponseRedirect(instance.get_absolute_url())
def get_instance(self, key, user):
"""Get object based on signed token.
instance, new_owner = (
signing.loads(key, max_age=self.max_age,
except signing.BadSignature as e:
logger.error('Tried invalid token. Token: %s, user: %s. %s',
key, unicode(user), unicode(e))
raise SuspiciousOperation()
except ValueError as e:
logger.error('Tried invalid token. Token: %s, user: %s. %s',
key, unicode(user), unicode(e))
raise SuspiciousOperation()
except TypeError as e:
logger.error('Tried invalid token. Token: %s, user: %s. %s',
key, unicode(user), unicode(e))
raise SuspiciousOperation()
instance = Instance.objects.get(id=instance)
except Instance.DoesNotExist as e:
logger.error('Tried token to nonexistent instance %d. '
'Token: %s, user: %s. %s',
instance, key, unicode(user), unicode(e))
raise Http404()
if new_owner !=
logger.error('%s (%d) tried the token for %s. Token: %s.',
unicode(user),, new_owner, key)
raise PermissionDenied()
return (instance, new_owner)
# flake8: noqa
from .activity import NodeActivity
from .activity import InstanceActivity
from .activity import instance_activity
from .instance import InstanceActiveManager
from .instance import BaseResourceConfigModel
from .instance import NamedBaseResourceConfig
......@@ -18,5 +19,6 @@ from .node import Lease
__all__ = [
'InstanceActivity', 'InstanceActiveManager', 'BaseResourceConfigModel',
'NamedBaseResourceConfig', 'VirtualMachineDescModel', 'InstanceTemplate',
'Instance', 'post_state_changed', 'pre_state_changed', 'InterfaceTemplate',
'Interface', 'Trait', 'Node', 'NodeActivity', 'Lease', ]
'Instance', 'instance_activity', 'post_state_changed', 'pre_state_changed',
'InterfaceTemplate', 'Interface', 'Trait', 'Node', 'NodeActivity', 'Lease',
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 sign in to comment