Commit 2b75c2fb by Kálmán Viktor

Merge branch 'issue-159' into 'master'

Revoke instance and instancetemplate access

Also known as "VM Details remove ACL implement"
parents 68a128a5 3bf6afcd
...@@ -499,3 +499,8 @@ footer a, footer a:hover, footer a:visited { ...@@ -499,3 +499,8 @@ footer a, footer a:hover, footer a:visited {
.table-sorting { .table-sorting {
display: none; display: none;
} }
#vm-access-table th:last-child, #vm-access-table td:last-child,
#template-access-table th:last-child, #template-access-table td:last-child {
text-align: center;
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
{% block content %} {% block content %}
<div class="row"> <div class="row">
<div class="col-md-8"> <div class="col-md-7">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<a class="pull-right btn btn-default btn-xs" href="{% url "dashboard.views.template-list" %}">{% trans "Back" %}</a> <a class="pull-right btn btn-default btn-xs" href="{% url "dashboard.views.template-list" %}">{% trans "Back" %}</a>
...@@ -23,33 +23,51 @@ ...@@ -23,33 +23,51 @@
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-5">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h4 class="no-margin"><i class="icon-group"></i> {% trans "Manage access" %}</h4> <h4 class="no-margin"><i class="icon-group"></i> {% trans "Manage access" %}</h4>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<form action="{% url "dashboard.views.template-acl" pk=object.pk %}" method="post">{% csrf_token %} <form action="{% url "dashboard.views.template-acl" pk=object.pk %}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields"> <table class="table table-striped table-with-form-fields" id="template-access-table">
<thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th></th></tr></thead> <thead>
<tr>
<th></th>
<th>{% trans "Who" %}</th>
<th>{% trans "What" %}</th>
<th><i class="icon-remove"></i></th>
</tr></thead>
<tbody> <tbody>
{% for i in acl.users %} {% for i in acl.users %}
<tr><td><i class="icon-user"></i></td><td>{{i.user}}</td> <tr>
<td><select class="form-control" name="perm-u-{{i.user.id}}"> <td><i class="icon-user"></i></td><td>{{i.user}}</td>
{% for id, name in acl.levels %} <td>
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option> <select class="form-control" name="perm-u-{{i.user.id}}">
{% endfor %} {% for id, name in acl.levels %}
</select></td> <option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
<td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr> {% endfor %}
</select>
</td>
<td>
<input type="checkbox" name="remove-u-{{i.user.id}}" title="{% trans "Remove" %}"/>
</td>
</tr>
{% endfor %} {% endfor %}
{% for i in acl.groups %} {% for i in acl.groups %}
<tr><td><i class="icon-group"></i></td><td>{{i.group}}</td> <tr>
<td><select class="form-control" name="perm-g-{{i.group.id}}"> <td><i class="icon-group"></i></td><td>{{i.group}}</td>
{% for id, name in acl.levels %} <td>
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option> <select class="form-control" name="perm-g-{{i.group.id}}">
{% endfor %} {% for id, name in acl.levels %}
</select></td> <option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
<td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr> {% endfor %}
</select>
</td>
<td>
<input type="checkbox" name="remove-g-{{i.group.id}}" title="{% trans "Remove" %}"/>
</td>
</tr>
{% endfor %} {% endfor %}
<tr><td><i class="icon-plus"></i></td> <tr><td><i class="icon-plus"></i></td>
<td><input type="text" class="form-control" name="perm-new-name" <td><input type="text" class="form-control" name="perm-new-name"
......
...@@ -15,26 +15,43 @@ ...@@ -15,26 +15,43 @@
</p> </p>
<h3>{% trans "Permissions"|capfirst %}</h3> <h3>{% trans "Permissions"|capfirst %}</h3>
<form action="{{acl.url}}" method="post">{% csrf_token %} <form action="{{acl.url}}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields"> <table class="table table-striped table-with-form-fields" id="vm-access-table">
<thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th></th></tr></thead> <thead><tr>
<th></th>
<th>{% trans "Who" %}</th>
<th>{% trans "What" %}</th>
<th>{% trans "Remove" %}</th>
</tr></thead>
<tbody> <tbody>
{% for i in acl.users %} {% for i in acl.users %}
<tr><td><i class="icon-user"></i></td><td>{{i.user}}</td> <tr>
<td><select class="form-control" name="perm-u-{{i.user.id}}"> <td><i class="icon-user"></i></td>
{% for id, name in acl.levels %} <td>{{i.user}}</td>
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option> <td>
{% endfor %} <select class="form-control" name="perm-u-{{i.user.id}}">
</select></td> {% for id, name in acl.levels %}
<td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr> <option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select>
</td>
<td>
<input type="checkbox" name="remove-u-{{i.user.id}}"/>
</td>
</tr>
{% endfor %} {% endfor %}
{% for i in acl.groups %} {% for i in acl.groups %}
<tr><td><i class="icon-group"></i></td><td>{{i.group}}</td> <tr>
<td><select class="form-control" name="perm-g-{{i.group.id}}"> <td><i class="icon-group"></i></td><td>{{i.group}}</td>
{% for id, name in acl.levels %} <td>
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option> <select class="form-control" name="perm-g-{{i.group.id}}">
{% endfor %} {% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select></td> </select></td>
<td><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr> <td>
<input type="checkbox" name="remove-g-{{i.group.id}}"/>
</td>
</tr>
{% endfor %} {% endfor %}
<tr><td><i class="icon-plus"></i></td> <tr><td><i class="icon-plus"></i></td>
<td><input type="text" class="form-control" name="perm-new-name" <td><input type="text" class="form-control" name="perm-new-name"
......
...@@ -1150,3 +1150,121 @@ class ProfileViewTest(LoginMixin, TestCase): ...@@ -1150,3 +1150,121 @@ class ProfileViewTest(LoginMixin, TestCase):
self.assertIsNotNone(authenticate(username="user1", self.assertIsNotNone(authenticate(username="user1",
password="password")) password="password"))
self.assertIsNone(authenticate(username="user1", password="asd")) self.assertIsNone(authenticate(username="user1", password="asd"))
class AclViewTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
def setUp(self):
Instance.get_remote_queue_name = Mock(return_value='test')
self.u1 = User.objects.create(username='user1')
self.u1.set_password('password')
self.u1.save()
self.u2 = User.objects.create(username='user2', is_staff=True)
self.u2.set_password('password')
self.u2.save()
self.us = User.objects.create(username='superuser', is_superuser=True)
self.us.set_password('password')
self.us.save()
self.ut = User.objects.get(username="test")
self.g1 = Group.objects.create(name='group1')
self.g1.user_set.add(self.u1)
self.g1.user_set.add(self.u2)
self.g1.save()
settings["default_vlangroup"] = 'public'
VlanGroup.objects.create(name='public')
def tearDown(self):
super(AclViewTest, self).tearDown()
self.u1.delete()
self.u2.delete()
self.us.delete()
self.g1.delete()
def test_permitted_instance_access_revoke(self):
c = Client()
# this is from the fixtures
self.login(c, "test", "test")
inst = Instance.objects.get(id=1)
inst.set_level(self.u1, "user")
resp = c.post("/dashboard/vm/1/acl/", {
'remove-u-%d' % self.u1.pk: "",
'perm-new-name': "",
'perm-new': "",
})
self.assertFalse((self.u1, "user") in inst.get_users_with_level())
self.assertEqual(resp.status_code, 302)
def test_unpermitted_instance_access_revoke(self):
c = Client()
self.login(c, self.u2)
inst = Instance.objects.get(id=1)
inst.set_level(self.u1, "user")
resp = c.post("/dashboard/vm/1/acl/", {
'remove-u-%d' % self.u1.pk: "",
'perm-new-name': "",
'perm-new': "",
})
self.assertTrue((self.u1, "user") in inst.get_users_with_level())
self.assertEqual(resp.status_code, 403)
def test_instance_original_owner_access_revoke(self):
c = Client()
self.login(c, self.u1)
inst = Instance.objects.get(id=1)
inst.set_level(self.u1, "owner")
inst.set_level(self.ut, "owner")
resp = c.post("/dashboard/vm/1/acl/", {
'remove-u-%d' % self.ut.pk: "",
'perm-new-name': "",
'perm-new': "",
})
self.assertEqual(self.ut, Instance.objects.get(id=1).owner)
self.assertTrue((self.ut, "owner") in inst.get_users_with_level())
self.assertEqual(resp.status_code, 302)
def test_permitted_template_access_revoke(self):
c = Client()
# this is from the fixtures
self.login(c, "test", "test")
tmpl = InstanceTemplate.objects.get(id=1)
tmpl.set_level(self.u1, "user")
resp = c.post("/dashboard/template/1/acl/", {
'remove-u-%d' % self.u1.pk: "",
'perm-new-name': "",
'perm-new': "",
})
self.assertFalse((self.u1, "user") in tmpl.get_users_with_level())
self.assertEqual(resp.status_code, 302)
def test_unpermitted_template_access_revoke(self):
c = Client()
self.login(c, self.u2)
tmpl = InstanceTemplate.objects.get(id=1)
tmpl.set_level(self.u1, "user")
resp = c.post("/dashboard/template/1/acl/", {
'remove-u-%d' % self.u1.pk: "",
'perm-new-name': "",
'perm-new': "",
})
self.assertTrue((self.u1, "user") in tmpl.get_users_with_level())
self.assertEqual(resp.status_code, 403)
def test_template_original_owner_access_revoke(self):
c = Client()
self.login(c, self.u1)
tmpl = InstanceTemplate.objects.get(id=1)
tmpl.set_level(self.u1, "owner")
tmpl.set_level(self.ut, "owner")
resp = c.post("/dashboard/template/1/acl/", {
'remove-u-%d' % self.ut.pk: "",
'perm-new-name': "",
'perm-new': "",
})
self.assertEqual(self.ut, InstanceTemplate.objects.get(id=1).owner)
self.assertTrue((self.ut, "owner") in tmpl.get_users_with_level())
self.assertEqual(resp.status_code, 302)
...@@ -714,8 +714,9 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin): ...@@ -714,8 +714,9 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin):
unicode(instance), unicode(request.user)) unicode(instance), unicode(request.user))
raise PermissionDenied() raise PermissionDenied()
self.set_levels(request, instance) self.set_levels(request, instance)
self.remove_levels(request, instance)
self.add_levels(request, instance) self.add_levels(request, instance)
return redirect(instance) return redirect("%s#access" % instance.get_absolute_url())
def set_levels(self, request, instance): def set_levels(self, request, instance):
for key, value in request.POST.items(): for key, value in request.POST.items():
...@@ -732,6 +733,24 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin): ...@@ -732,6 +733,24 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin):
unicode(entity), unicode(instance), unicode(entity), unicode(instance),
value, unicode(request.user)) value, unicode(request.user))
def remove_levels(self, request, instance):
for key, value in request.POST.items():
if key.startswith("remove"):
typ = key[7:8] # len("remove-")
id = key[9:] # len("remove-x-")
entity = {'u': User, 'g': Group}[typ].objects.get(id=id)
if getattr(instance, "owner", None) == entity:
logger.info("Tried to remove owner from %s by %s.",
unicode(instance), unicode(request.user))
msg = _("The original owner cannot be removed, however "
"you can transfer ownership!")
messages.warning(request, msg)
continue
instance.set_level(entity, None)
logger.info("Revoked %s's access to %s by %s.",
unicode(entity), unicode(instance),
unicode(request.user))
def add_levels(self, request, instance): def add_levels(self, request, instance):
name = request.POST['perm-new-name'] name = request.POST['perm-new-name']
value = request.POST['perm-new'] value = request.POST['perm-new']
...@@ -772,6 +791,7 @@ class TemplateAclUpdateView(AclUpdateView): ...@@ -772,6 +791,7 @@ class TemplateAclUpdateView(AclUpdateView):
else: else:
self.set_levels(request, template) self.set_levels(request, template)
self.add_levels(request, template) self.add_levels(request, template)
self.remove_levels(request, template)
post_for_disk = request.POST.copy() post_for_disk = request.POST.copy()
post_for_disk['perm-new'] = 'user' post_for_disk['perm-new'] = 'user'
...@@ -779,8 +799,7 @@ class TemplateAclUpdateView(AclUpdateView): ...@@ -779,8 +799,7 @@ class TemplateAclUpdateView(AclUpdateView):
for d in template.disks.all(): for d in template.disks.all():
self.add_levels(request, d) self.add_levels(request, d)
return redirect(reverse("dashboard.views.template-detail", return redirect(template)
kwargs=self.kwargs))
class GroupAclUpdateView(AclUpdateView): class GroupAclUpdateView(AclUpdateView):
......
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