Commit 387eb4e8 by Bach Dániel

Merge branch 'feature-node-permissions' into 'master'

Feature node permissions

Allow users with view_statistics to check the graphs and informations of the nodes.

See merge request !259
parents d0f4347b 76d043aa
...@@ -74,9 +74,11 @@ ...@@ -74,9 +74,11 @@
{% trans "list" %} {% trans "list" %}
{% endif %} {% endif %}
</a> </a>
{% if request.user.is_superuser %}
<a class="btn btn-success btn-xs node-create" href="{% url "dashboard.views.node-create" %}"> <a class="btn btn-success btn-xs node-create" href="{% url "dashboard.views.node-create" %}">
<i class="fa fa-plus-circle"></i> {% trans "new" %} <i class="fa fa-plus-circle"></i> {% trans "new" %}
</a> </a>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
</div> </div>
{% endif %} {% endif %}
{% if user.is_superuser %} {% if perms.vm.view_statistics %}
<div class="col-lg-4 col-sm-6"> <div class="col-lg-4 col-sm-6">
{% include "dashboard/index-nodes.html" %} {% include "dashboard/index-nodes.html" %}
</div> </div>
......
...@@ -6,14 +6,16 @@ ...@@ -6,14 +6,16 @@
{% block content %} {% block content %}
<div class="body-content"> <div class="body-content">
<div class="page-header"> <div class="page-header">
{% if request.user.is_superuser %}
<div class="pull-right" id="ops"> <div class="pull-right" id="ops">
{% include "dashboard/vm-detail/_operations.html" %} {% include "dashboard/vm-detail/_operations.html" %}
</div> </div>
<div class="pull-right" style="padding-top: 15px;"> <div class="pull-right" style="padding-top: 15px;">
<a title="{% trans "Rename" %}" href="#" class="btn btn-default btn-xs node-details-rename-button"><i class="fa fa-pencil"></i></a> <a title="{% trans "Rename" %}" href="#" class="btn btn-default btn-xs node-details-rename-button"><i class="fa fa-pencil"></i></a>
<a title="{% trans "Delete" %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-delete" href="{% url "dashboard.views.delete-node" pk=node.pk %}"><i class="fa fa-trash-o"></i></a> <a title="{% trans "Delete" %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-delete" href="{% url "dashboard.views.delete-node" pk=node.pk %}"><i class="fa fa-trash-o"></i></a>
</div> </div>
<h1> {% endif %}
<h1>
<div id="node-details-rename"> <div id="node-details-rename">
<form action="" method="POST" id="node-details-rename-form"> <form action="" method="POST" id="node-details-rename-form">
{% csrf_token %} {% csrf_token %}
...@@ -69,26 +71,26 @@ ...@@ -69,26 +71,26 @@
{% trans "Resources" %} {% trans "Resources" %}
</a> </a>
</li> </li>
<li> <li>
<a href="{% url "dashboard.views.vm-list" %}?s=node:{{ node.name }}" <a href="{% url "dashboard.views.vm-list" %}?s=node:{{ node.name }}"
target="blank" class="text-center"> target="blank" class="text-center">
<i class="fa fa-desktop fa-2x"></i><br> <i class="fa fa-desktop fa-2x"></i><br>
{% trans "Virtual Machines" %} {% trans "Virtual Machines" %}
</a> </a>
</li> </li>
<li> <li>
<a href="#activity" data-toggle="pill" class="text-center"> <a href="#activity" data-toggle="pill" class="text-center">
<i class="fa fa-clock-o fa-2x"></i><br> <i class="fa fa-clock-o fa-2x"></i><br>
{% trans "Activity" %} {% trans "Activity" %}
</a> </a>
</li> </li>
</ul> </ul>
<div id="panel-body" class="tab-content panel-body"> <div id="panel-body" class="tab-content panel-body">
<div class="tab-pane active" id="home">{% include "dashboard/node-detail/home.html" %}</div> <div class="tab-pane active" id="home">{% include "dashboard/node-detail/home.html" %}</div>
<div class="tab-pane" id="resources">{% include "dashboard/node-detail/resources.html" %}</div> <div class="tab-pane" id="resources">{% include "dashboard/node-detail/resources.html" %}</div>
<div class="tab-pane" id="activity">{% include "dashboard/node-detail/activity.html" %}</div> <div class="tab-pane" id="activity">{% include "dashboard/node-detail/activity.html" %}</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -16,16 +16,19 @@ ...@@ -16,16 +16,19 @@
{% endif %} {% endif %}
</div> </div>
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
<style> <style>
.row { .row {
margin-bottom: 15px; margin-bottom: 15px;
} }
</style> </style>
<form action="{% url "dashboard.views.node-addtrait" node.pk %}" method="POST"> {% if request.user.is_superuser %}
{% csrf_token %} <form action="{% url "dashboard.views.node-addtrait" node.pk %}" method="POST">
{% crispy trait_form %} {% csrf_token %}
</form> {% crispy trait_form %}
</form>
{% endif %}
</div><!-- id:node-details-traits --> </div><!-- id:node-details-traits -->
</div> </div>
<div class="col-md-8"> <div class="col-md-8">
......
...@@ -18,10 +18,12 @@ ...@@ -18,10 +18,12 @@
<dt>{% trans "Host name" %}:</dt> <dt>{% trans "Host name" %}:</dt>
<dd> <dd>
{{ node.host.hostname }} {{ node.host.hostname }}
{% if request.user.is_superuser %}
<a href="{{ node.host.get_absolute_url }}" class="btn btn-default btn-xs"> <a href="{{ node.host.get_absolute_url }}" class="btn btn-default btn-xs">
<i class="fa fa-pencil"></i> <i class="fa fa-pencil"></i>
{% trans "Edit host" %} {% trans "Edit host" %}
</a> </a>
{% endif %}
</dd> </dd>
</dl> </dl>
......
...@@ -637,7 +637,7 @@ class NodeDetailTest(LoginMixin, TestCase): ...@@ -637,7 +637,7 @@ class NodeDetailTest(LoginMixin, TestCase):
c = Client() c = Client()
self.login(c, 'user1') self.login(c, 'user1')
response = c.get('/dashboard/node/25555/') response = c.get('/dashboard/node/25555/')
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 403)
def test_anon_node_page(self): def test_anon_node_page(self):
c = Client() c = Client()
...@@ -667,7 +667,7 @@ class NodeDetailTest(LoginMixin, TestCase): ...@@ -667,7 +667,7 @@ class NodeDetailTest(LoginMixin, TestCase):
node = Node.objects.get(pk=1) node = Node.objects.get(pk=1)
old_name = node.name old_name = node.name
response = c.post("/dashboard/node/1/", {'new_name': 'test1235'}) response = c.post("/dashboard/node/1/", {'new_name': 'test1235'})
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 403)
self.assertEqual(Node.objects.get(pk=1).name, old_name) self.assertEqual(Node.objects.get(pk=1).name, old_name)
def test_permitted_set_name(self): def test_permitted_set_name(self):
...@@ -721,7 +721,7 @@ class NodeDetailTest(LoginMixin, TestCase): ...@@ -721,7 +721,7 @@ class NodeDetailTest(LoginMixin, TestCase):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
response = c.post("/dashboard/node/1/", {'to_remove': traitid}) response = c.post("/dashboard/node/1/", {'to_remove': traitid})
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 403)
self.assertEqual(Node.objects.get(pk=1).traits.count(), trait_count) self.assertEqual(Node.objects.get(pk=1).traits.count(), trait_count)
def test_permitted_remove_trait(self): def test_permitted_remove_trait(self):
......
...@@ -26,7 +26,7 @@ from django.http import HttpResponse, Http404 ...@@ -26,7 +26,7 @@ from django.http import HttpResponse, Http404
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import View from django.views.generic import View
from braces.views import LoginRequiredMixin, SuperuserRequiredMixin from braces.views import LoginRequiredMixin
from vm.models import Instance, Node from vm.models import Instance, Node
...@@ -142,22 +142,28 @@ class VmGraphView(GraphViewBase): ...@@ -142,22 +142,28 @@ class VmGraphView(GraphViewBase):
base = VmMetric base = VmMetric
class NodeGraphView(SuperuserRequiredMixin, GraphViewBase): class NodeGraphView(GraphViewBase):
model = Node model = Node
base = NodeMetric base = NodeMetric
def get_object(self, request, pk): def get_object(self, request, pk):
if not self.request.user.has_perm('vm.view_statistics'):
raise PermissionDenied()
return self.model.objects.get(id=pk) return self.model.objects.get(id=pk)
class NodeListGraphView(SuperuserRequiredMixin, GraphViewBase): class NodeListGraphView(GraphViewBase):
model = Node model = Node
base = Metric base = Metric
def get_object(self, request, pk): def get_object(self, request, pk):
if not self.request.user.has_perm('vm.view_statistics'):
raise PermissionDenied()
return Node.objects.filter(enabled=True) return Node.objects.filter(enabled=True)
def get(self, request, metric, time, *args, **kwargs): def get(self, request, metric, time, *args, **kwargs):
if not self.request.user.has_perm('vm.view_statistics'):
raise PermissionDenied()
return super(NodeListGraphView, self).get(request, None, metric, time) return super(NodeListGraphView, self).get(request, None, metric, time)
......
...@@ -62,7 +62,7 @@ class IndexView(LoginRequiredMixin, TemplateView): ...@@ -62,7 +62,7 @@ class IndexView(LoginRequiredMixin, TemplateView):
}) })
# nodes # nodes
if user.is_superuser: if user.has_perm('vm.view_statistics'):
nodes = Node.objects.all() nodes = Node.objects.all()
context.update({ context.update({
'nodes': nodes[:5], 'nodes': nodes[:5],
......
...@@ -75,13 +75,18 @@ node_ops = OrderedDict([ ...@@ -75,13 +75,18 @@ node_ops = OrderedDict([
]) ])
class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, class NodeDetailView(LoginRequiredMixin,
GraphMixin, DetailView): GraphMixin, DetailView):
template_name = "dashboard/node-detail.html" template_name = "dashboard/node-detail.html"
model = Node model = Node
form = None form = None
form_class = TraitForm form_class = TraitForm
def get(self, *args, **kwargs):
if not self.request.user.has_perm('vm.view_statistics'):
raise PermissionDenied()
return super(NodeDetailView, self).get(*args, **kwargs)
def get_context_data(self, form=None, **kwargs): def get_context_data(self, form=None, **kwargs):
if form is None: if form is None:
form = self.form_class() form = self.form_class()
...@@ -98,6 +103,8 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, ...@@ -98,6 +103,8 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin,
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
if not request.user.is_superuser:
raise PermissionDenied()
if request.POST.get('new_name'): if request.POST.get('new_name'):
return self.__set_name(request) return self.__set_name(request)
if request.POST.get('to_remove'): if request.POST.get('to_remove'):
...@@ -145,13 +152,14 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, ...@@ -145,13 +152,14 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin,
return redirect(self.object.get_absolute_url()) return redirect(self.object.get_absolute_url())
class NodeList(LoginRequiredMixin, SuperuserRequiredMixin, class NodeList(LoginRequiredMixin, GraphMixin, SingleTableView):
GraphMixin, SingleTableView):
template_name = "dashboard/node-list.html" template_name = "dashboard/node-list.html"
table_class = NodeListTable table_class = NodeListTable
table_pagination = False table_pagination = False
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
if not self.request.user.has_perm('vm.view_statistics'):
raise PermissionDenied()
if self.request.is_ajax(): if self.request.is_ajax():
nodes = Node.objects.all() nodes = Node.objects.all()
nodes = [{ nodes = [{
......
...@@ -88,7 +88,9 @@ class Node(OperatedMixin, TimeStampedModel): ...@@ -88,7 +88,9 @@ class Node(OperatedMixin, TimeStampedModel):
class Meta: class Meta:
app_label = 'vm' app_label = 'vm'
db_table = 'vm_node' db_table = 'vm_node'
permissions = () permissions = (
('view_statistics', _('Can view Node box and statistics.')),
)
ordering = ('-enabled', 'normalized_name') ordering = ('-enabled', 'normalized_name')
def __unicode__(self): def __unicode__(self):
......
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