Commit 3cf09597 by Bach Dániel

Merge branch 'feature-group-details-fixed'

Conflicts:
	circle/dashboard/static/dashboard/dashboard.js
	circle/dashboard/urls.py
parents 2d8071b0 9871de77
......@@ -19,11 +19,11 @@ from __future__ import absolute_import
from datetime import timedelta
from django.contrib.auth.models import User
from django.contrib.auth.forms import (
AuthenticationForm, PasswordResetForm, SetPasswordForm,
PasswordChangeForm,
)
from django.contrib.auth.models import User, Group
from crispy_forms.helper import FormHelper
from crispy_forms.layout import (
......@@ -312,6 +312,55 @@ class VmCustomizeForm(forms.Form):
)
class GroupCreateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(GroupCreateForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_show_labels = False
self.helper.layout = Layout(
Div(
Div(
AnyTag(
'h4',
HTML(_("Name")),
),
css_class="col-sm-10",
),
css_class="row",
),
Div(
Div(
Field('name', id="group-create-name"),
css_class="col-sm-10",
),
css_class="row",
),
Div( # buttons
Div(
AnyTag( # tip: don't try to use Button class
"button",
AnyTag(
"i",
css_class="icon-play"
),
HTML(" Create"),
css_id="vm-create-submit",
css_class="btn btn-success",
),
css_class="col-sm-5",
),
css_class="row",
),
)
class Meta:
model = Group
fields = ['name', ]
class HostForm(forms.ModelForm):
def setowner(self, user):
......
......@@ -26,7 +26,7 @@ from django.contrib.auth.signals import user_logged_in
from django.core.urlresolvers import reverse
from django.db.models import (
Model, ForeignKey, OneToOneField, CharField, IntegerField, TextField,
DateTimeField,
DateTimeField, permalink,
)
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _, override, ugettext
......@@ -111,6 +111,11 @@ class GroupProfile(AclBase):
except cls.DoesNotExist:
return Group.objects.get(name=name)
@permalink
def get_absolute_url(self):
return ('dashboard.views.group-detail', None,
{'pk': self.group.pk})
def get_or_create_profile(self):
obj, created = GroupProfile.objects.get_or_create(group_id=self.pk)
......
......@@ -607,3 +607,26 @@ footer a, footer a:hover, footer a:visited {
#notifications-button {
margin: 0;
}
#group-detail-user-table td:first-child, #group-detail-user-table th:last-child,
#group-detail-user-table td:last-child,
#group-detail-perm-table td:first-child, #group-detail-perm-table th:last-child,
#group-detail-perm-table td:last-child {
text-align: center;
width: 100px;
}
#group-detail-perm-header {
margin-top: 25px;
}
textarea[name="list-new-namelist"] {
max-width: 500px;
min-height: 80px;
margin-bottom: 10px;
}
/* 2px border bottom for all bootstrap tables */
.table thead>tr>th {
border-bottom: 1px;
}
......@@ -34,6 +34,22 @@ $(function () {
return false;
});
$('.group-create').click(function(e) {
$.ajax({
type: 'GET',
url: '/dashboard/group/create/',
success: function(data) {
$('body').append(data);
addSliderMiscs();
$('#create-modal').modal('show');
$('#create-modal').on('hidden.bs.modal', function() {
$('#create-modal').remove();
});
}
});
return false;
});
$('.template-choose').click(function(e) {
$.ajax({
type: 'GET',
......
/* rename */
$("#group-details-h1-name, .group-details-rename-button").click(function() {
$("#group-details-h1-name").hide();
......@@ -30,4 +29,38 @@
$(".group-details-help").stop().slideToggle();
});
/* for Node removes buttons */
$('.delete-from-group').click(function() {
var href = $(this).attr('href');
var tr = $(this).closest('tr');
var group = $(this).data('group_pk');
var member = $(this).data('member_pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(removeMember,
{ 'url': href,
'data': [],
'tr': tr,
'group_pk': group,
'member_pk': member,
'type': "user",
'redirect': dir});
return false;
});
function removeMember(data) {
$.ajax({
type: 'POST',
url: data['url'],
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(re, textStatus, xhr) {
data['tr'].fadeOut(function() {
$(this).remove();});
},
error: function(xhr, textStatus, error) {
addMessage('Uh oh :(', 'danger')
}
});
}
......@@ -99,7 +99,7 @@ $(function() {
$("#group-list-column-name", row).html(
$("<a/>", {
'class': "real-link",
href: "/dashboard/group/" + data['node_pk'] + "/",
href: "/dashboard/group/" + data['group_pk'] + "/",
text: data['new_name']
})
).show();
......
......@@ -134,16 +134,19 @@ class GroupListTable(Table):
)
number_of_users = TemplateColumn(
orderable=False,
template_name='dashboard/group-list/column-users.html',
attrs={'th': {'class': 'group-list-table-admin'}},
)
admin = TemplateColumn(
orderable=False,
template_name='dashboard/group-list/column-admin.html',
attrs={'th': {'class': 'group-list-table-admin'}},
)
actions = TemplateColumn(
orderable=False,
attrs={'th': {'class': 'group-list-table-thin'}},
template_code=('{% include "dashboard/group-list/column-'
'actions.html" with btn_size="btn-xs" %}'),
......
{% load i18n %}
<div class="modal fade" id="confirmation-modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
{% if text %}
{{ text }}
{% else %}
{%blocktrans with object=object%}
Are you sure you want to remove <strong>{{ member }}</strong> from <strong>{{ object }}</strong>?
{%endblocktrans%}
{% endif %}
<br />
<div class="pull-right" style="margin-top: 15px;">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button id="confirmation-modal-button" type="button" class="btn btn-warning">Remove</button>
</div>
<div class="clearfix"></div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div>
{% load crispy_forms_tags %}
<style>
.row {
margin-bottom: 15px;
}
</style>
<form method="POST" action="/dashboard/group/create/">
{% csrf_token %}
{% crispy form %}
</form>
......@@ -37,51 +37,92 @@
<div class="row">
<div class="col-md-12" id="group-detail-pane">
<div class="panel panel-default" id="group-detail-panel">
<div class="tab-content panel-body">
<h3>{% trans "User list"|capfirst %}</h3>
<table class="table table-striped table-with-form-fields">
<div class="tab-content panel-body" id="group-form-body">
<h3>{% trans "User list"|capfirst %}</h3>
<form action="" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields table-bordered" id="group-detail-user-table">
<tbody>
<thead><tr><th></th><th>{% trans "Who" %}</th><th></th><th></th></tr></thead>
<thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "Remove" %}</th></tr></thead>
{% for i in users %}
<tr><td><i class="icon-user"></i></td><td>{{i.username}}</td>
<td><a data-group-pk="{{ i.pk }}" href="#" class="real-link groupuser-delete btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr>
<tr>
<td><i class="icon-user"></i></td><td>{{i.username}}</td>
<td>
<a data-group_pk="{{ group.pk }}" data-member_pk="{{i.pk}}" href="{% url "dashboard.views.remove-user" member_pk=i.pk group_pk=group.pk %}" class="real-link delete-from-group btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a>
</td>
</tr>
{% endfor %}
<tr><td><i class="icon-plus"></i></td>
<td><input type="text" class="form-control" name="perm-new-name"
placeholder="{% trans "Name of group or user" %}"></td>
<tr>
<td><i class="icon-plus"></i></td>
<td colspan="2">
<input type="text" class="form-control" name="list-new-name"placeholder="{% trans "Name of user" %}">
</td>
</tr>
</tbody>
</table>
<textarea name="list-new-namelist" class="form-control"
placeholder="{% trans "List of usernames (one per line)." %}"></textarea>
<div class="form-actions">
<button type="submit" class="btn btn-success">{% trans "Save" %}</button>
</div>
</form>
<h3>{% trans "Permissions"|capfirst %}</h3>
<h3 id="group-detail-perm-header">{% trans "Permissions"|capfirst %}</h3>
<form action="{{acl.url}}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields">
<thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th></th></tr></thead>
<table class="table table-striped table-with-form-fields table-bordered" id="group-detail-perm-table">
<thead>
<tr>
<th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th>{% trans "Remove" %}</th>
</tr>
</thead>
<tbody>
{% for i in acl.users %}
<tr><td><i class="icon-user"></i></td><td>{{i.user}}</td>
<td><select class="form-control" name="perm-u-{{i.user.id}}">
{% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select></td>
<td class="user-remove"><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr>
{% for i in acl.users %}
<tr>
<td><i class="icon-user"></i></td><td>{{i.user}}</td>
<td>
<select class="form-control" name="perm-u-{{i.user.id}}">
{% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
<tr><td><i class="icon-plus"></i></td>
<td><input type="text" class="form-control" name="perm-new-name"
placeholder="{% trans "Name of group or user" %}"></td>
<td><select class="form-control" name="perm-new">
{% for id, name in acl.levels %}
<option value="{{id}}">{{name}}</option>
{% endfor %}
</select></td><td></td>
</tr>
</select>
</td>
<td class="user-remove"><a data-group_pk="{{ group.pk }}" data-member_pk="{{i.user.pk }}" href="{% url "dashboard.views.remove-acluser" member_pk=i.user.pk group_pk=group.pk %}" class="real-link delete-from-group btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td>
</tr>
{% endfor %}
{% for i in acl.groups %}
<tr>
<td><i class="icon-group"></i></td><td>{{ i.group }}</td>
<td>
<select class="form-control" name="perm-g-{{ i.group.pk }}">
{% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select>
</td>
<td class="user-remove"><a data-group_pk="{{ i.pk }}"data-member_pk="{{i.group.pk }}" href="{% url "dashboard.views.remove-aclgroup" member_pk=i.group.pk group_pk=group.pk %}" class="real-link delete-from-group btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a>
</td>
</tr>
{% endfor %}
<tr>
<td><i class="icon-plus"></i></td>
<td>
<input type="text" class="form-control" name="perm-new-name"
placeholder="{% trans "Name of group or user" %}">
</td>
<td>
<select class="form-control" name="perm-new">
{% for id, name in acl.levels %}
<option value="{{id}}">{{name}}</option>
{% endfor %}
</select>
</td>
<td></td>
</tr>
</tbody>
</table>
<textarea class="form-control"></textarea>
<div class="form-actions panel-body">
<div class="form-actions">
<button type="submit" class="btn btn-success">{% trans "Save" %}</button>
</div>
</form>
......
......@@ -26,7 +26,7 @@
<i class="icon-chevron-sign-right"></i> <strong>{{ more_groups }}</strong> more
</a>
{% endif %}
<a class="btn btn-success btn-xs group-create" href="#"><i class="icon-upload-alt"></i> {% trans "new" %} </a>
<a class="btn btn-success btn-xs group-create" href="{% url "dashboard.views.group-create" %}"><i class="icon-upload-alt"></i> {% trans "new" %} </a>
</div>
</div>
</div>
......
......@@ -17,7 +17,7 @@
</div>
{% endif %}
{% if perms.group %}
{% if perms.auth %}
<div class="col-lg-4 col-sm-6">
{% include "dashboard/index-groups.html" %}
</div>
......
......@@ -871,6 +871,611 @@ class NodeDetailTest(LoginMixin, TestCase):
self.assertEqual(node_enabled, not Node.objects.get(pk=1).enabled)
class GroupCreateTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
def setUp(self):
# u0 - user with creating group permissions
self.u0 = User.objects.create(username='user0')
self.u0.set_password('password')
self.u0.save()
permlist = Permission.objects.all()
self.u0.user_permissions.add(
filter(lambda element: 'group' in element.name and
'add' in element.name, permlist)[0])
# u1 simple user without permissions
self.u1 = User.objects.create(username='user1')
self.u1.set_password('password')
self.u1.save()
self.us = User.objects.create(username='superuser', is_superuser=True)
self.us.set_password('password')
self.us.save()
self.g1 = Group.objects.create(name='group1')
self.g1.save()
def tearDown(self):
super(GroupCreateTest, self).tearDown()
self.g1.delete()
self.u0.delete()
self.u1.delete()
self.us.delete()
def test_anon_group_page(self):
c = Client()
response = c.get('/dashboard/group/create/')
self.assertEqual(response.status_code, 302)
def test_superuser_group_page(self):
c = Client()
self.login(c, 'superuser')
response = c.get('/dashboard/group/create/')
self.assertEqual(response.status_code, 200)
def test_permitted_group_page(self):
c = Client()
self.login(c, 'user0')
response = c.get('/dashboard/group/create/')
self.assertEqual(response.status_code, 200)
def test_unpermitted_group_page(self):
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/create/')
self.assertEqual(response.status_code, 403)
def test_anon_group_create(self):
c = Client()
groupnum = Group.objects.count()
response = c.post('/dashboard/group/create/', {'name': 'newgroup'})
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum)
def test_unpermitted_group_create(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'user1')
response = c.post('/dashboard/group/create/', {'name': 'newgroup'})
self.assertEqual(response.status_code, 403)
self.assertEqual(Group.objects.count(), groupnum)
def test_permitted_group_create(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'user0')
response = c.post('/dashboard/group/create/', {'name': 'newgroup'})
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum + 1)
def test_superuser_group_create(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'superuser')
response = c.post('/dashboard/group/create/', {'name': 'newgroup'})
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum + 1)
def test_namecollision_group_create(self):
# hint: group1 is in setUp, the tests checks creating group with the
# same name
c = Client()
groupnum = Group.objects.count()
self.login(c, 'superuser')
response = c.post('/dashboard/group/create/', {'name': 'group1'})
self.assertEqual(response.status_code, 200)
self.assertEqual(Group.objects.count(), groupnum)
def test_creator_is_owner_when_group_create(self):
# has owner rights in the group the user who created the group?
c = Client()
self.login(c, 'user0')
c.post('/dashboard/group/create/', {'name': 'newgroup'})
newgroup = Group.objects.get(name='newgroup')
self.assertTrue(newgroup.profile.has_level(self.u0, 'owner'))
class GroupDeleteTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
def setUp(self):
# u0 - user with creating group permissions
self.u0 = User.objects.create(username='user0')
self.u0.set_password('password')
self.u0.save()
permlist = Permission.objects.all()
self.u0.user_permissions.add(
filter(lambda element: 'group' in element.name and
'delete' in element.name, permlist)[0])
# u1 simple user without permissions
self.u1 = User.objects.create(username='user1')
self.u1.set_password('password')
self.u1.save()
self.us = User.objects.create(username='superuser', is_superuser=True)
self.us.set_password('password')
self.us.save()
self.g1 = Group.objects.create(name='group1')
self.g1.profile.set_user_level(self.u0, 'owner')
self.g1.save()
def tearDown(self):
super(GroupDeleteTest, self).tearDown()
self.g1.delete()
self.u0.delete()
self.u1.delete()
self.us.delete()
def test_anon_group_page(self):
c = Client()
response = c.get('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
def test_superuser_group_page(self):
c = Client()
self.login(c, 'superuser')
response = c.get('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_permitted_group_page(self):
c = Client()
self.login(c, 'user0')
response = c.get('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_unpermitted_group_page(self):
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
def test_anon_group_delete(self):
c = Client()
groupnum = Group.objects.count()
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum)
def test_unpermitted_group_delete(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'user1')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
self.assertEqual(Group.objects.count(), groupnum)
def test_permitted_group_delete(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'user0')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum - 1)
def test_superuser_group_delete(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'superuser')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum - 1)
class GroupDetailTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
def setUp(self):
Instance.get_remote_queue_name = Mock(return_value='test')
# u0 - owner for group1
self.u0 = User.objects.create(username='user0')
self.u0.set_password('password')
self.u0.save()
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.u3 = User.objects.create(username='user3')
self.u3.set_password('password')
self.u3.save()
# u4 - removable user for group1
self.u4 = User.objects.create(username='user4')
self.u4.set_password('password')
self.u4.save()
self.us = User.objects.create(username='superuser', is_superuser=True)
self.us.set_password('password')
self.us.save()
self.g1 = Group.objects.create(name='group1')
self.g1.profile.set_user_level(self.u0, 'owner')
self.g1.profile.set_user_level(self.u4, 'operator')
self.g1.user_set.add(self.u4)
self.g1.save()
self.g2 = Group.objects.create(name='group2')
self.g2.save()
self.g3 = Group.objects.create(name='group3')
self.g3.save()
self.g1.profile.set_group_level(self.g3, 'operator')
settings["default_vlangroup"] = 'public'
VlanGroup.objects.create(name='public')
def tearDown(self):
super(GroupDetailTest, self).tearDown()
self.g1.delete()
self.g2.delete()
self.g3.delete()
self.u0.delete()
self.u1.delete()
self.u2.delete()
self.us.delete()
self.u3.delete()
self.u4.delete()
def test_404_superuser_group_page(self):
c = Client()
self.login(c, 'superuser')
response = c.get('/dashboard/group/25555/')
self.assertEqual(response.status_code, 404)
def test_404_user_group_page(self):
c = Client()
self.login(c, 'user0')
response = c.get('/dashboard/group/25555/')
self.assertEqual(response.status_code, 404)
def test_anon_group_page(self):
c = Client()
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
def test_superuser_group_page(self):
c = Client()
self.login(c, 'superuser')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_acluser_group_page(self):
c = Client()
self.login(c, 'user0')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_acluser2_group_page(self):
self.g1.profile.set_user_level(self.u1, 'operator')
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_unpermitted_user_group_page(self):
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
def test_user_in_userlist_group_page(self):
self.g1.user_set.add(self.u1)
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
def test_groupmember_group_page(self):
self.g2.user_set.add(self.u1)
self.g1.profile.set_group_level(self.g2, 'owner')
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_superuser_group_delete(self):
num_of_groups = Group.objects.count()
c = Client()
self.login(c, 'superuser')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), num_of_groups - 1)
def test_unpermitted_group_delete(self):
num_of_groups = Group.objects.count()
c = Client()
self.login(c, 'user3')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
self.assertEqual(Group.objects.count(), num_of_groups)
def test_acl_group_delete(self):
num_of_groups = Group.objects.count()
c = Client()
self.login(c, 'user0')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), num_of_groups - 1)
def test_anon_group_delete(self):
num_of_groups = Group.objects.count()
c = Client()
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), num_of_groups)
# add to group
def test_anon_add_user_to_group(self):
c = Client()
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/', {'list-new-name': 'user3'})
self.assertEqual(user_in_group,
self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_unpermitted_add_user_to_group(self):
c = Client()
self.login(c, 'user3')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/', {'list-new-name': 'user3'})
self.assertEqual(user_in_group, self.g1.user_set.count())
self.assertEqual(response.status_code, 403)
def test_superuser_add_user_to_group(self):
c = Client()
self.login(c, 'superuser')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/', {'list-new-name': 'user3'})
self.assertEqual(user_in_group + 1, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_permitted_add_user_to_group(self):
c = Client()
self.login(c, 'user0')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/', {'list-new-name': 'user3'})
self.assertEqual(user_in_group + 1, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_permitted_add_multipleuser_to_group(self):
c = Client()
self.login(c, 'user0')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/',
{'list-new-namelist': 'user1\r\nuser2'})
self.assertEqual(user_in_group + 2, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_add_multipleuser_skip_badname_to_group(self):
c = Client()
self.login(c, 'user0')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/',
{'list-new-namelist': 'user1\r\nnoname\r\nuser2'})
self.assertEqual(user_in_group + 2, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_unpermitted_add_multipleuser_to_group(self):
c = Client()
self.login(c, 'user3')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/',
{'list-new-namelist': 'user1\r\nuser2'})
self.assertEqual(user_in_group, self.g1.user_set.count())
self.assertEqual(response.status_code, 403)
def test_anon_add_multipleuser_to_group(self):
c = Client()
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/',
{'list-new-namelist': 'user1\r\nuser2'})
self.assertEqual(user_in_group, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_anon_add_acluser_to_group(self):
c = Client()
gp = self.g1.profile
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'user3', 'perm-new': 'owner'})
self.assertEqual(acl_users, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_unpermitted_add_acluser_to_group(self):
c = Client()
self.login(c, 'user3')
gp = self.g1.profile
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'user3', 'perm-new': 'owner'})
self.assertEqual(acl_users, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 403)
def test_superuser_add_acluser_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'superuser')
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'user3', 'perm-new': 'owner'})
self.assertEqual(acl_users + 1, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_permitted_add_acluser_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'user0')
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'user3', 'perm-new': 'owner'})
self.assertEqual(acl_users + 1, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_anon_add_aclgroup_to_group(self):
c = Client()
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'group2', 'perm-new': 'owner'})
self.assertEqual(acl_groups, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
def test_unpermitted_add_aclgroup_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'user3')
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'group2', 'perm-new': 'owner'})
self.assertEqual(acl_groups, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 403)
def test_superuser_add_aclgroup_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'superuser')
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'group2', 'perm-new': 'owner'})
self.assertEqual(acl_groups + 1, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
def test_permitted_add_aclgroup_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'user0')
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'group2', 'perm-new': 'owner'})
self.assertEqual(acl_groups + 1, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
# remove from group
def test_anon_remove_user_from_group(self):
c = Client()
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/user/' + str(self.u4.pk) + '/')
self.assertEqual(user_in_group,
self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_unpermitted_remove_user_from_group(self):
c = Client()
self.login(c, 'user3')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/user/' + str(self.u4.pk) + '/')
self.assertEqual(user_in_group, self.g1.user_set.count())
self.assertEqual(response.status_code, 403)
def test_superuser_remove_user_from_group(self):
c = Client()
self.login(c, 'superuser')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/user/' + str(self.u4.pk) + '/')
self.assertEqual(user_in_group - 1, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_permitted_remove_user_from_group(self):
c = Client()
self.login(c, 'user0')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/user/' + str(self.u4.pk) + '/')
self.assertEqual(user_in_group - 1, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_anon_remove_acluser_from_group(self):
c = Client()
gp = self.g1.profile
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/user/' + str(self.u4.pk) + '/')
self.assertEqual(acl_users, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_unpermitted_remove_acluser_from_group(self):
c = Client()
self.login(c, 'user3')
gp = self.g1.profile
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/user/' + str(self.u4.pk) + '/')
self.assertEqual(acl_users, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 403)
def test_superuser_remove_acluser_from_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'superuser')
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/user/' + str(self.u4.pk) + '/')
self.assertEqual(acl_users - 1, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_permitted_remove_acluser_from_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'user0')
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/user/' + str(self.u4.pk) + '/')
self.assertEqual(acl_users - 1, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_anon_remove_aclgroup_from_group(self):
c = Client()
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/group/' + str(self.g3.pk) + '/')
self.assertEqual(acl_groups, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
def test_unpermitted_remove_aclgroup_from_group(self):
c = Client()
self.login(c, 'user3')
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/group/' + str(self.g3.pk) + '/')
self.assertEqual(acl_groups, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 403)
def test_superuser_remove_aclgroup_from_group(self):
c = Client()
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
self.login(c, 'superuser')
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/group/' + str(self.g3.pk) + '/')
self.assertEqual(acl_groups - 1, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
def test_permitted_remove_aclgroup_from_group(self):
c = Client()
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
self.login(c, 'user0')
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/group/' + str(self.g3.pk) + '/')
self.assertEqual(acl_groups - 1, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
class VmDetailVncTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
......
......@@ -21,7 +21,7 @@ from django.conf.urls import patterns, url, include
from vm.models import Instance
from .views import (
AclUpdateView, DiskAddView, FavouriteView, GroupAclUpdateView, GroupDelete,
GroupDetailView, GroupList, GroupUserDelete, IndexView,
GroupDetailView, GroupList, IndexView,
InstanceActivityDetail, LeaseCreate, LeaseDelete, LeaseDetail,
MyPreferencesView, NodeAddTraitView, NodeCreate, NodeDelete,
NodeDetailView, NodeFlushView, NodeGraphView, NodeList, NodeStatus,
......@@ -30,6 +30,8 @@ from .views import (
TransferOwnershipView, vm_activity, VmCreate, VmDelete, VmDetailView,
VmDetailVncTokenView, VmGraphView, VmList, VmMassDelete, VmMigrateView,
VmRenewView, DiskRemoveView, get_disk_download_status, InterfaceDeleteView,
GroupRemoveAclUserView, GroupRemoveAclGroupView, GroupRemoveUserView,
GroupCreate,
TemplateChoose,
)
......@@ -116,9 +118,6 @@ urlpatterns = patterns(
name='dashboard.views.group-detail'),
url(r'^group/(?P<pk>\d+)/acl/$', GroupAclUpdateView.as_view(),
name='dashboard.views.group-acl'),
url(r'^groupuser/delete/(?P<pk>\d+)/$', GroupUserDelete.as_view(),
name="dashboard.views.delete-groupuser"),
url(r'^notifications/$', NotificationView.as_view(),
name="dashboard.views.notifications"),
......@@ -134,4 +133,15 @@ urlpatterns = patterns(
url(r'^profile/$', MyPreferencesView.as_view(),
name="dashboard.views.profile"),
url(r'^group/(?P<group_pk>\d+)/remove/acl/user/(?P<member_pk>\d+)/$',
GroupRemoveAclUserView.as_view(),
name="dashboard.views.remove-acluser"),
url(r'^group/(?P<group_pk>\d+)/remove/acl/group/(?P<member_pk>\d+)/$',
GroupRemoveAclGroupView.as_view(),
name="dashboard.views.remove-aclgroup"),
url(r'^group/(?P<group_pk>\d+)/remove/user/(?P<member_pk>\d+)/$',
GroupRemoveUserView.as_view(),
name="dashboard.views.remove-user"),
url(r'^group/create/$', GroupCreate.as_view(),
name='dashboard.views.group-create'),
)
......@@ -52,18 +52,21 @@ from braces.views._access import AccessMixin
from .forms import (
CircleAuthenticationForm, DiskAddForm, HostForm, LeaseForm, MyProfileForm,
NodeForm, TemplateForm, TraitForm, VmCustomizeForm,
NodeForm, TemplateForm, TraitForm, VmCustomizeForm, GroupCreateForm,
CirclePasswordChangeForm
)
from .tables import (NodeListTable, NodeVmListTable,
TemplateListTable, LeaseListTable, GroupListTable,)
from .tables import (
NodeListTable, NodeVmListTable, TemplateListTable, LeaseListTable,
GroupListTable,
)
from vm.models import (
Instance, instance_activity, InstanceActivity, InstanceTemplate, Interface,
InterfaceTemplate, Lease, Node, NodeActivity, Trait,
)
from storage.models import Disk
from firewall.models import Vlan, Host, Rule
from .models import Favourite, Profile
from .models import Favourite, Profile, GroupProfile
logger = logging.getLogger(__name__)
......@@ -140,11 +143,13 @@ class IndexView(LoginRequiredMixin, TemplateView):
})
# groups
groups = Group.objects.all()
context.update({
'groups': groups[:5],
'more_groups': groups.count() - len(groups[:5]),
})
if user.has_module_perms('auth'):
profiles = GroupProfile.get_objects_with_level('operator', user)
groups = Group.objects.filter(groupprofile__in=profiles)
context.update({
'groups': groups[:5],
'more_groups': groups.count() - len(groups[:5]),
})
# template
if user.has_perm('vm.create_template'):
......@@ -672,6 +677,7 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView):
class GroupDetailView(CheckedDetailView):
template_name = "dashboard/group-detail.html"
model = Group
read_level = 'operator'
def get_has_level(self):
return self.object.profile.has_level
......@@ -684,12 +690,45 @@ class GroupDetailView(CheckedDetailView):
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
if not self.get_has_level()(request.user, 'operator'):
raise PermissionDenied()
if request.POST.get('new_name'):
return self.__set_name(request)
if request.POST.get('list-new-name'):
return self.__add_user(request)
if request.POST.get('list-new-namelist'):
return self.__add_list(request)
if (request.POST.get('list-new-name') is not None) and \
(request.POST.get('list-new-namelist') is not None):
return redirect(reverse_lazy("dashboard.views.group-detail",
kwargs={'pk': self.get_object().pk}))
def __add_user(self, request):
name = request.POST['list-new-name']
self.__add_username(request, name)
return redirect(reverse_lazy("dashboard.views.group-detail",
kwargs={'pk': self.object.pk}))
def __add_username(self, request, name):
if not name:
return
try:
entity = User.objects.get(username=name)
self.object.user_set.add(entity)
except User.DoesNotExist:
warning(request, _('User "%s" not found.') % name)
def __add_list(self, request):
if not self.get_has_level()(request.user, 'operator'):
raise PermissionDenied()
userlist = request.POST.get('list-new-namelist').split('\r\n')
for line in userlist:
self.__add_username(request, line)
return redirect(reverse_lazy("dashboard.views.group-detail",
kwargs={'pk': self.object.pk}))
def __set_name(self, request):
self.object = self.get_object()
new_name = request.POST.get("new_name")
Group.objects.filter(pk=self.object.pk).update(
**{'name': new_name})
......@@ -827,27 +866,6 @@ class GroupAclUpdateView(AclUpdateView):
else:
self.set_levels(request, instance)
self.add_levels(request, instance)
# return redirect(self.profile)
return redirect(reverse("dashboard.views.group-detail",
kwargs=self.kwargs))
def repost(self, request, *args, **kwargs):
group = self.get_object()
if not (group.profile.has_level(request.user, "owner") or
getattr(group.profile, 'owner', None) == request.user):
logger.warning('Tried to set permissions of %s by non-owner %s.',
unicode(group), unicode(request.user))
raise PermissionDenied()
name = request.POST['perm-new-name']
if (User.objects.filter(username=name).count() +
Group.objects.filter(name=name).count() < 1
and len(name) > 0):
warning(request, _('User or group "%s" not found.') % name)
else:
self.set_levels(request, group.profile)
self.add_levels(request, group.profile)
return redirect(reverse("dashboard.views.group-detail",
kwargs=self.kwargs))
......@@ -1130,43 +1148,87 @@ class NodeList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
number_of_VMs=Count('instance_set')).select_related('host')
class GroupList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
class GroupList(LoginRequiredMixin, SingleTableView):
template_name = "dashboard/group-list.html"
model = Group
table_class = GroupListTable
table_pagination = False
def get(self, *args, **kwargs):
if self.request.is_ajax():
groups = [{
'url': reverse("dashboard.views.group-detail",
kwargs={'pk': i.pk}),
'name': i.name} for i in self.queryset]
return HttpResponse(
json.dumps(list(groups)),
content_type="application/json",
)
else:
return super(GroupList, self).get(*args, **kwargs)
def get_queryset(self):
logger.debug('GroupList.get_queryset() called. User: %s',
unicode(self.request.user))
profiles = GroupProfile.get_objects_with_level(
'operator', self.request.user)
groups = Group.objects.filter(groupprofile__in=profiles)
s = self.request.GET.get("s")
if s:
groups = groups.filter(name__icontains=s)
return groups
class GroupUserDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
"""This stuff deletes the group.
"""
model = User
template_name = "dashboard/confirm/base-delete.html"
class GroupRemoveUserView(CheckedDetailView, DeleteView):
model = Group
slug_field = 'pk'
slug_url_kwarg = 'group_pk'
read_level = 'operator'
def get_has_level(self):
return self.object.profile.has_level
def get_context_data(self, **kwargs):
context = super(GroupRemoveUserView, self).get_context_data(**kwargs)
try:
context['member'] = User.objects.get(pk=self.member_pk)
except User.DoesNotExist:
raise Http404()
return context
def get_success_url(self):
next = self.request.POST.get('next')
if next:
return next
else:
return reverse_lazy("dashboard.views.group-detail",
kwargs={'pk': self.get_object().pk})
def get(self, request, member_pk, *args, **kwargs):
self.member_pk = member_pk
return super(GroupRemoveUserView, self).get(request, *args, **kwargs)
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/confirm/ajax-delete.html']
return ['dashboard/confirm/ajax-remove.html']
else:
return ['dashboard/confirm/base-delete.html']
return ['dashboard/confirm/base-remove.html']
def get_context_data(self, **kwargs):
# this is redundant now, but if we wanna add more to print
# we'll need this
context = super(GroupUserDelete, self).get_context_data(**kwargs)
return context
def remove_member(self, pk):
container = self.get_object()
container.user_set.remove(User.objects.get(pk=pk))
def get_success_message(self):
return _("Member successfully removed from group!")
# github.com/django/django/blob/master/django/views/generic/edit.py#L245
def delete(self, request, *args, **kwargs):
object = self.get_object()
object.delete()
if not object.profile.has_level(request.user, 'operator'):
raise PermissionDenied()
self.remove_member(kwargs["member_pk"])
success_url = self.get_success_url()
success_message = _("Group successfully deleted!")
success_message = self.get_success_message()
if request.is_ajax():
if request.POST.get('redirect').lower() == "true":
messages.success(request, success_message)
return HttpResponse(
json.dumps({'message': success_message}),
content_type="application/json",
......@@ -1175,20 +1237,45 @@ class GroupUserDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
messages.success(request, success_message)
return HttpResponseRedirect(success_url)
def get_success_url(self):
next = self.request.POST.get('next')
if next:
return next
else:
return reverse_lazy('dashboard.index')
class GroupRemoveAclUserView(GroupRemoveUserView):
class GroupDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
def remove_member(self, pk):
container = self.get_object().profile
container.set_level(User.objects.get(pk=pk), None)
def get_success_message(self):
return _("Acl user successfully removed from group!")
class GroupRemoveAclGroupView(GroupRemoveUserView):
def get_context_data(self, **kwargs):
context = super(GroupRemoveUserView, self).get_context_data(**kwargs)
try:
context['member'] = Group.objects.get(pk=self.member_pk)
except User.DoesNotExist:
raise Http404()
return context
def remove_member(self, pk):
container = self.get_object().profile
container.set_level(Group.objects.get(pk=pk), None)
def get_success_message(self):
return _("Acl group successfully removed from group!")
class GroupDelete(CheckedDetailView, DeleteView):
"""This stuff deletes the group.
"""
model = Group
template_name = "dashboard/confirm/base-delete.html"
read_level = 'operator'
def get_has_level(self):
return self.object.profile.has_level
def get_template_names(self):
if self.request.is_ajax():
......@@ -1196,16 +1283,11 @@ class GroupDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
else:
return ['dashboard/confirm/base-delete.html']
def get_context_data(self, **kwargs):
# this is redundant now, but if we wanna add more to print
# we'll need this
context = super(GroupDelete, self).get_context_data(**kwargs)
return context
# github.com/django/django/blob/master/django/views/generic/edit.py#L245
def delete(self, request, *args, **kwargs):
object = self.get_object()
if not object.profile.has_level(request.user, 'owner'):
raise PermissionDenied()
object.delete()
success_url = self.get_success_url()
success_message = _("Group successfully deleted!")
......@@ -1431,6 +1513,49 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
return redirect(path)
class GroupCreate(LoginRequiredMixin, TemplateView):
form_class = GroupCreateForm
form = None
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/modal-wrapper.html']
else:
return ['dashboard/nojs-wrapper.html']
def get(self, request, form=None, *args, **kwargs):
if not request.user.has_module_perms('auth'):
raise PermissionDenied()
if form is None:
form = self.form_class()
context = self.get_context_data(**kwargs)
context.update({
'template': 'dashboard/group-create.html',
'box_title': 'Create a Group',
'form': form,
})
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
if not request.user.has_module_perms('auth'):
raise PermissionDenied()
form = self.form_class(request.POST)
if not form.is_valid():
return self.get(request, form, *args, **kwargs)
form.cleaned_data
savedform = form.save()
savedform.profile.set_level(request.user, 'owner')
messages.success(request, _('Group successfully created!'))
if request.is_ajax():
return HttpResponse(json.dumps({'redirect':
savedform.profile.get_absolute_url()}),
content_type="application/json")
else:
return redirect(savedform.profile.get_absolute_url())
class VmDelete(LoginRequiredMixin, DeleteView):
model = Instance
template_name = "dashboard/confirm/base-delete.html"
......
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