Commit 4c29a7bb by Szabolcs Gelencser

Implement template share with users

parent 0ec3899c
...@@ -433,11 +433,17 @@ class TemplateForm(forms.ModelForm): ...@@ -433,11 +433,17 @@ class TemplateForm(forms.ModelForm):
# })) # }))
name = forms.TextInput() name = forms.TextInput()
flavor = forms.ChoiceField(['a','b','c']) # flavor = forms.ChoiceField(['a','b','c'])
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user", None) self.user = kwargs.pop("user", None)
super(TemplateForm, self).__init__(*args, **kwargs) super(TemplateForm, self).__init__(*args, **kwargs)
self.allowed_fields = (
'name',
# 'lease',
)
# #
# self.fields['networks'].queryset = ()#Vlan.get_objects_with_level('user', self.user) # self.fields['networks'].queryset = ()#Vlan.get_objects_with_level('user', self.user)
# #
...@@ -487,34 +493,34 @@ class TemplateForm(forms.ModelForm): ...@@ -487,34 +493,34 @@ class TemplateForm(forms.ModelForm):
def clean_max_ram_size(self): def clean_max_ram_size(self):
return self.cleaned_data.get("ram_size", 0) return self.cleaned_data.get("ram_size", 0)
def _clean_fields(self): # def _clean_fields(self):
try: # try:
old = InstanceTemplate.objects.get(pk=self.instance.pk) # old = InstanceTemplate.objects.get(pk=self.instance.pk)
except InstanceTemplate.DoesNotExist: # except InstanceTemplate.DoesNotExist:
old = None # old = None
for name, field in self.fields.items(): # for name, field in self.fields.items():
if name in self.allowed_fields: # if name in self.allowed_fields:
value = field.widget.value_from_datadict( # value = field.widget.value_from_datadict(
self.data, self.files, self.add_prefix(name)) # self.data, self.files, self.add_prefix(name))
try: # try:
value = field.clean(value) # value = field.clean(value)
self.cleaned_data[name] = value # self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name): # if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)() # value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value # self.cleaned_data[name] = value
except ValidationError as e: # except ValidationError as e:
self._errors[name] = self.error_class(e.messages) # self._errors[name] = self.error_class(e.messages)
if name in self.cleaned_data: # if name in self.cleaned_data:
del self.cleaned_data[name] # del self.cleaned_data[name]
elif old: # elif old:
if name == 'networks': # if name == 'networks':
self.cleaned_data[name] = [ # self.cleaned_data[name] = [
i.vlan for i in self.instance.interface_set.all()] # i.vlan for i in self.instance.interface_set.all()]
else: # else:
self.cleaned_data[name] = getattr(old, name) # self.cleaned_data[name] = getattr(old, name)
if "req_traits" not in self.allowed_fields: # if "req_traits" not in self.allowed_fields:
self.cleaned_data['req_traits'] = self.instance.req_traits.all() # self.cleaned_data['req_traits'] = self.instance.req_traits.all()
def save(self, commit=True): def save(self, commit=True):
data = self.cleaned_data data = self.cleaned_data
...@@ -1300,12 +1306,7 @@ class UserEditForm(forms.ModelForm): ...@@ -1300,12 +1306,7 @@ class UserEditForm(forms.ModelForm):
class AclUserOrGroupAddForm(forms.Form): class AclUserOrGroupAddForm(forms.Form):
name = forms.CharField( name = forms.CharField(required=False)
widget=autocomplete.ListSelect2(
url='autocomplete.acl.user-group',
attrs={'class': 'form-control',
'data-html': 'true',
'data-placeholder': _("Name of group or user")}))
class TransferOwnershipForm(forms.Form): class TransferOwnershipForm(forms.Form):
......
{% load i18n %} {% load i18n %}
<form action="{{ acl.url }}" method="post">{% csrf_token %} <form action="{{ manage_access_url }}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields acl-table" id="{{table_id}}"> <table class="table table-striped table-with-form-fields acl-table" id="{{table_id}}">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
<th>{% trans "Who" %}</th> <th>{% trans "Who" %}</th>
<th>{% trans "What" %}</th>
<th><i id="manage-access-select-all" class="fa fa-times"></i></th> <th><i id="manage-access-select-all" class="fa fa-times"></i></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for i in acl.users %} {% for member in shared_with_users %}
<tr> <tr>
<td> <td>
<img class="profile-avatar" src="{{ i.user.profile.get_avatar_url }}"/> {# <img class="profile-avatar" src="{{ i.user.profile.get_avatar_url }}"/>#}
</td> </td>
<td> <td>
<a href="{% url "dashboard.views.profile" username=i.user.username %}" {# <a href="{% url "dashboard.views.profile" username=i.user.username %}"#}
title="{{ i.user.username }}"> {# title="{{ i.user.username }}">#}
{% include "dashboard/_display-name.html" with user=i.user show_org=True %} {# {% include "dashboard/_display-name.html" with user=i.user show_org=True %}#}
</a> {# </a>#}
</td> {{ member.username }}
<td>
<select class="form-control" name="perm-u-{{i.user.id}}"{% if i.level not in acl.allowed_levels %} disabled{% endif %}>
{% for id, name in acl.levels %}
<option{%if id == i.level%} selected="selected"{%endif%}
{% if id not in acl.allowed_levels %} disabled{% endif %}
value="{{id}}">{{name}}</option>
{% endfor %}
</select>
</td> </td>
<td> <td>
<input type="checkbox" name="remove-u-{{i.user.id}}" title="{% trans "Remove" %}"/> <input type="checkbox" name="remove-u-{{ member.member_id }}" title="{% trans "Remove" %}"/>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
...@@ -57,15 +48,10 @@ ...@@ -57,15 +48,10 @@
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
<tr><td><i class="fa fa-plus"></i></td> <tr>
<td>{{aclform.name }}</td> <td><i class="fa fa-plus"></i></td>
<td><select class="form-control" name="level"> <td>{{aclform.name }}</td>
{% for id, name in acl.levels %} <td></td>
{% if id in acl.allowed_levels %}
<option value="{{id}}">{{name}}</option>
{% endif %}
{% endfor %}
</select></td><td></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
......
...@@ -42,8 +42,8 @@ ...@@ -42,8 +42,8 @@
{# {{ form.max_ram_size|as_crispy_field }}#} {# {{ form.max_ram_size|as_crispy_field }}#}
</fieldset> </fieldset>
<fieldset> {# <fieldset>#}
<legend>{% trans "Virtual machine settings" %}</legend> {# <legend>{% trans "Virtual machine settings" %}</legend>#}
{# {{ form.arch|as_crispy_field }}#} {# {{ form.arch|as_crispy_field }}#}
{# {{ form.access_method|as_crispy_field }}#} {# {{ form.access_method|as_crispy_field }}#}
{# {{ form.boot_menu|as_crispy_field }}#} {# {{ form.boot_menu|as_crispy_field }}#}
...@@ -52,11 +52,11 @@ ...@@ -52,11 +52,11 @@
{# {{ form.description|as_crispy_field }}#} {# {{ form.description|as_crispy_field }}#}
{# {{ form.system|as_crispy_field }}#} {# {{ form.system|as_crispy_field }}#}
{# {{ form.has_agent|as_crispy_field }}#} {# {{ form.has_agent|as_crispy_field }}#}
</fieldset> {# </fieldset>#}
<fieldset> <fieldset>
<legend>{% trans "External resources" %}</legend> <legend>{% trans "External resources" %}</legend>
{# {{ form.networks|as_crispy_field }}#} {# {{ form.networks|as_crispy_field }}#}
{# {{ form.lease|as_crispy_field }}#} {{ form.lease|as_crispy_field }}
{##} {##}
{# {{ form.tags|as_crispy_field }}#} {# {{ form.tags|as_crispy_field }}#}
</fieldset> </fieldset>
...@@ -82,26 +82,6 @@ ...@@ -82,26 +82,6 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h4 class="no-margin"><i class="fa fa-user"></i> {% trans "Owner" %}</h4>
</div>
<div class="panel-body">
{% if user == object.owner %}
{% blocktrans %}You are the current owner of this template.{% endblocktrans %}
{% else %}
{% url "dashboard.views.profile" username=object.owner.username as url %}
{% blocktrans with owner=object.owner name=object.owner.get_full_name%}
The current owner of this template is <a href="{{url}}">{{name}} ({{owner}})</a>.
{% endblocktrans %}
{% endif %}
{% if user == object.owner or user.is_superuser %}
<a href="{% url "dashboard.views.template-transfer-ownership" object.pk %}"
class="btn btn-link tx-tpl-ownership">{% trans "Transfer ownership..." %}</a>
{% endif %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="no-margin"><i class="fa fa-group"></i> {% trans "Manage access" %}</h4> <h4 class="no-margin"><i class="fa fa-group"></i> {% trans "Manage access" %}</h4>
</div> </div>
<div class="panel-body"> <div class="panel-body">
...@@ -111,40 +91,6 @@ ...@@ -111,40 +91,6 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h4 class="no-margin">
<i class="fa fa-question-circle"></i>
{% trans "Access level rights" %}
</h4>
</div>
<div class="panel-body">
<dl>
<dt>{% trans "User" %}</dt>
<dd>
{% blocktrans %}
User can deploy instances from this template.
{% endblocktrans %}
</dd>
<dt>{% trans "Operator" %}</dt>
<dd>
{% blocktrans %}
Operators are able to deploy and grant/revoke User level access to this template.
{% endblocktrans %}
</dd>
<dt>{% trans "Owner" %}</dt>
<dd>
{% blocktrans %}
Owners can edit attributes or delete the template.
Owners are able to grant/revoke User, Operator and Owner level access to the template.
The accountable owner (the one who created the template) can not be demoted.
The accountable ownership can be transferred to other User via the "Transfer onwership" button.
{% endblocktrans %}
</dd>
</dl>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="no-margin"><i class="fa fa-file"></i> {% trans "Disk list" %}</h4> <h4 class="no-margin"><i class="fa fa-file"></i> {% trans "Disk list" %}</h4>
</div> </div>
<div class="panel-body"> <div class="panel-body">
......
...@@ -19,7 +19,8 @@ from __future__ import absolute_import ...@@ -19,7 +19,8 @@ from __future__ import absolute_import
from dashboard.views.autocomplete import AclUserGroupAutocomplete, AclUserAutocomplete from dashboard.views.autocomplete import AclUserGroupAutocomplete, AclUserAutocomplete
from dashboard.views.template import TemplateList, TemplateChoose, TemplateDetail, TemplateDelete, \ from dashboard.views.template import TemplateList, TemplateChoose, TemplateDetail, TemplateDelete, \
TransferTemplateOwnershipConfirmView, TransferTemplateOwnershipView, LeaseCreate, LeaseDetail, LeaseDelete TransferTemplateOwnershipConfirmView, TransferTemplateOwnershipView, LeaseCreate, LeaseDetail, LeaseDelete, \
TemplateAclUpdateView
from dashboard.views.vm import VmDetailView, VmList, VmCreate, vm_activity, vm_ops, FavouriteView, VmPlainImageCreate from dashboard.views.vm import VmDetailView, VmList, VmCreate, vm_activity, vm_ops, FavouriteView, VmPlainImageCreate
from django.conf.urls import url from django.conf.urls import url
...@@ -47,8 +48,8 @@ urlpatterns = [ ...@@ -47,8 +48,8 @@ urlpatterns = [
# name="dashboard.views.template-create"), # name="dashboard.views.template-create"),
url(r'^template/choose/$', TemplateChoose.as_view(), url(r'^template/choose/$', TemplateChoose.as_view(),
name="dashboard.views.template-choose"), name="dashboard.views.template-choose"),
# url(r'template/(?P<pk>\d+)/acl/$', TemplateAclUpdateView.as_view(), url(r'template/(?P<pk>\d+)/acl/$', TemplateAclUpdateView.as_view(),
# name='dashboard.views.template-acl'), name='dashboard.views.template-acl'),
url(r'^template/(?P<pk>\d+)/$', TemplateDetail.as_view(), url(r'^template/(?P<pk>\d+)/$', TemplateDetail.as_view(),
name='dashboard.views.template-detail'), name='dashboard.views.template-detail'),
url(r"^template/list/$", TemplateList.as_view(), url(r"^template/list/$", TemplateList.as_view(),
......
...@@ -89,10 +89,9 @@ class AclUserAutocomplete(autocomplete.Select2ListView): ...@@ -89,10 +89,9 @@ class AclUserAutocomplete(autocomplete.Select2ListView):
}), content_type="application/json") }), content_type="application/json")
class AclUserGroupAutocomplete(AclUserAutocomplete): class AclUserGroupAutocomplete(autocomplete.Select2ListView): # TODO: was AclUserAutocomplete inherited
group_search_fields = ('name', 'groupprofile__org_id')
def get_list(self): def get_list(self):
groups = AclUpdateView.get_allowed_groups(self.request.user) from openstack_api import keystone
groups = self.filter(groups, self.group_search_fields) groups = keystone.group_list(request=self.request, user=self.request.user)
return super(AclUserGroupAutocomplete, self).get_list() + groups group_names = [g.name for g in groups]
return super(AclUserGroupAutocomplete, self).get_list() + group_names
...@@ -41,6 +41,9 @@ from braces.views import ( ...@@ -41,6 +41,9 @@ from braces.views import (
) )
from django.views.generic.edit import BaseUpdateView, ModelFormMixin, FormView from django.views.generic.edit import BaseUpdateView, ModelFormMixin, FormView
from django_tables2 import SingleTableView from django_tables2 import SingleTableView
from keystoneauth1 import session
from keystoneauth1.identity import v3
from openstack_auth.utils import fix_auth_url_version
from vm.models import ( from vm.models import (
InstanceTemplate, InterfaceTemplate, Instance, Lease, InstanceActivity InstanceTemplate, InterfaceTemplate, Instance, Lease, InstanceActivity
...@@ -289,6 +292,8 @@ class TemplateDelete(DeleteViewBase): ...@@ -289,6 +292,8 @@ class TemplateDelete(DeleteViewBase):
object.delete() object.delete()
class TemplateDetail(LoginRequiredMixin, GraphMixin, SuccessMessageMixin, UpdateView): class TemplateDetail(LoginRequiredMixin, GraphMixin, SuccessMessageMixin, UpdateView):
model = InstanceTemplate model = InstanceTemplate
template_name = "dashboard/template-edit.html" template_name = "dashboard/template-edit.html"
...@@ -325,11 +330,57 @@ class TemplateDetail(LoginRequiredMixin, GraphMixin, SuccessMessageMixin, Update ...@@ -325,11 +330,57 @@ class TemplateDetail(LoginRequiredMixin, GraphMixin, SuccessMessageMixin, Update
else: else:
return super(TemplateDetail, self).get(request, *args, **kwargs) return super(TemplateDetail, self).get(request, *args, **kwargs)
def __get_glance_admin_client(self, request):
from keystoneauth1 import session
from glanceclient import Client
auth = v3.Password(
auth_url=fix_auth_url_version(settings.OPENSTACK_KEYSTONE_URL),
user_id=settings.OPENSTACK_CIRCLE_USERID,
password=settings.OPENSTACK_CIRCLE_PASSWORD,
project_id=request.user.tenant_id,
)
session = session.Session(auth=auth, verify=False)
return Client('2', session=session)
def __get_members_shared_with(self):
template = self.get_object()
glance = self.__get_glance_admin_client(self.request)
members_generator = glance.image_members.list(template.image_id)
return [m for m in members_generator]
def __get_project_client(self, project_id):
from keystoneclient.v3 import client
auth = v3.Password(
auth_url=fix_auth_url_version(settings.OPENSTACK_KEYSTONE_URL),
user_id=settings.OPENSTACK_CIRCLE_USERID,
password=settings.OPENSTACK_CIRCLE_PASSWORD,
project_id=project_id,
)
sess = session.Session(auth=auth, verify=False)
return client.Client(session=sess, interface='public')
def __get_username_for(self, project_id):
client = self.__get_project_client(project_id)
project = client.projects.get(project_id)
return project.name # as username = project's name
def __get_users_shared_with(self):
members = self.__get_members_shared_with()
for m in members:
m['username'] = self.__get_username_for(m.member_id)
return members
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
template = self.get_object() template = self.get_object()
context = super(TemplateDetail, self).get_context_data(**kwargs) context = super(TemplateDetail, self).get_context_data(**kwargs)
# context['acl'] = AclUpdateView.get_acl_data( # context['acl'] = AclUpdateView.get_acl_data(
# template, self.request.user, 'dashboard.views.template-acl') # template, self.request.user, 'dashboard.views.template-acl')
context['manage_access_url'] = reverse_lazy('dashboard.views.template-acl',
kwargs={'pk': template.id})
context['shared_with_users'] = self.__get_users_shared_with()
# context['disks'] = template.disks.all() # context['disks'] = template.disks.all()
context['is_owner'] = template.owner_id == self.request.user.id context['is_owner'] = template.owner_id == self.request.user.id
context['aclform'] = AclUserOrGroupAddForm() context['aclform'] = AclUserOrGroupAddForm()
...@@ -341,13 +392,14 @@ class TemplateDetail(LoginRequiredMixin, GraphMixin, SuccessMessageMixin, Update ...@@ -341,13 +392,14 @@ class TemplateDetail(LoginRequiredMixin, GraphMixin, SuccessMessageMixin, Update
return reverse_lazy("dashboard.views.template-detail", return reverse_lazy("dashboard.views.template-detail",
kwargs=self.kwargs) kwargs=self.kwargs)
def post(self, request, *args, **kwargs): def post(self, request):
template = self.get_object() template = self.get_object()
if not template.has_level(request.user, 'owner'): # TODO: multiple users
if template.owner_id != request.user.id:
raise PermissionDenied() raise PermissionDenied()
return super(TemplateDetail, self).post(self, request, args, kwargs) return super(TemplateDetail, self).post(self, request, args, kwargs)
def get_form_kwargs(self): def get_form_kwargs(self, *args, **kwargs):
kwargs = super(TemplateDetail, self).get_form_kwargs() kwargs = super(TemplateDetail, self).get_form_kwargs()
kwargs['user'] = self.request.user kwargs['user'] = self.request.user
return kwargs return kwargs
......
...@@ -43,6 +43,8 @@ from django.views.decorators.csrf import csrf_protect ...@@ -43,6 +43,8 @@ from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic import DetailView, View, DeleteView, FormView from django.views.generic import DetailView, View, DeleteView, FormView
from django.views.generic.detail import SingleObjectMixin from django.views.generic.detail import SingleObjectMixin
from keystoneauth1.identity import v3
from openstack_auth.utils import fix_auth_url_version
from vm.models import Instance from vm.models import Instance
from ..models import GroupProfile from ..models import GroupProfile
...@@ -411,159 +413,141 @@ class RequestFormOperationMixin(FormOperationMixin): ...@@ -411,159 +413,141 @@ class RequestFormOperationMixin(FormOperationMixin):
class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin): class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin):
def send_success_message(self, whom, old_level, new_level): # def send_success_message(self, whom, old_level, new_level):
if old_level and new_level: # if old_level and new_level:
msg = _("Acl user/group %(w)s successfully modified.") # msg = _("Acl user/group %(w)s successfully modified.")
elif not old_level and new_level: # elif not old_level and new_level:
msg = _("Acl user/group %(w)s successfully added.") # msg = _("Acl user/group %(w)s successfully added.")
elif old_level and not new_level: # elif old_level and not new_level:
msg = _("Acl user/group %(w)s successfully removed.") # msg = _("Acl user/group %(w)s successfully removed.")
if msg: # if msg:
messages.success(self.request, msg % {'w': whom}) # messages.success(self.request, msg % {'w': whom})
#
def get_level(self, whom): # def add_levels(self):
for u, level in self.acl_data: # self.request, _('User "%s" has already '
if u == whom: # 'access to this object.') % name)
return level # self.request, _('Group "%s" has already '
# 'access to this object.') % name)
# self.request, _('User or group "%s" not found.') % name)
def __get_current_users_groups(self, request):
from openstack_api import keystone
groups = keystone.group_list(request=self.request, user=self.request.user)
return [g.name for g in groups]
def __get_glance_admin_client(self, project_id):
from keystoneauth1 import session
from glanceclient import Client
auth = v3.Password(
auth_url=fix_auth_url_version(settings.OPENSTACK_KEYSTONE_URL),
user_id=settings.OPENSTACK_CIRCLE_USERID,
password=settings.OPENSTACK_CIRCLE_PASSWORD,
project_id=project_id,
)
session = session.Session(auth=auth, verify=False)
return Client('2', session=session)
def __get_all_projects(self):
from keystoneauth1 import session
from keystoneclient.v3 import client
auth = v3.Password(
auth_url=fix_auth_url_version(settings.OPENSTACK_KEYSTONE_URL),
user_id=settings.OPENSTACK_CIRCLE_USERID,
password=settings.OPENSTACK_CIRCLE_PASSWORD,
)
sess = session.Session(auth=auth, verify=False)
keystone = client.Client(session=sess, interface=settings.OPENSTACK_INTERFACE)
return keystone.projects.list(
domain=settings.OPENSTACK_CIRCLE_DOMAIN_ID,
user=settings.OPENSTACK_CIRCLE_USERID
)
def __get_project_id_by_name(self, request, name):
projects = self.__get_all_projects()
for p in projects:
if p.name == name:
return p.id
return None return None
@classmethod def __accept_membership(self, membership, project_id_of_user):
def get_acl_data(cls, obj, user, url): template = self.get_object()
levels = obj.ACL_LEVELS glance = self.__get_glance_admin_client(project_id_of_user)
allowed_levels = list(l for l in OrderedDict(levels) glance.image_members.update(template.image_id, project_id_of_user, 'accepted')
if cls.has_next_level(user, obj, l))
is_owner = 'owner' in allowed_levels def __handle_group_assignment(self, request):
template = self.get_object()
allowed_users = cls.get_allowed_users(user) pass
allowed_groups = (set(cls.get_allowed_groups(user)) |
set(user.groups.all())) def __handle_user_assignment(self, request):
glance = self.__get_glance_admin_client(request.user.tenant_id)
user_levels = list( template = self.get_object()
{'user': u, 'level': l} for u, l in obj.get_users_with_level() new_template_user = request.POST['name']
if is_owner or u == user or u in allowed_users) project_id_of_user = self.__get_project_id_by_name(
request, new_template_user
group_levels = list( )
{'group': g, 'level': l} for g, l in obj.get_groups_with_level()
if is_owner or g in allowed_groups) old_members_generator = glance.image_members.list(template.image_id)
old_members = [m.member_id for m in old_members_generator]
return {'users': user_levels,
'groups': group_levels, if project_id_of_user in old_members:
'levels': levels, msg = _("Template is already shared with %s" % new_template_user)
'allowed_levels': allowed_levels, messages.warning(self.request, msg)
'url': reverse(url, args=[obj.pk])} elif project_id_of_user is not None:
membership = glance.image_members.create(template.image_id, project_id_of_user)
self.__accept_membership(membership, project_id_of_user)
msg = _("Successfully shared with %s" % new_template_user)
messages.success(self.request, msg)
else:
msg = _("User or group with name '%s' doesn't exist" % new_template_user)
messages.error(self.request, msg)
@classmethod def __handle_assignments(self, request):
def has_next_level(self, user, instance, level): current_users_groups = self.__get_current_users_groups(request)
levels = OrderedDict(instance.ACL_LEVELS).keys() new_template_user = request.POST['name']
next_levels = dict(zip([None] + levels, levels + levels[-1:]))
# {None: 'user', 'user': 'operator', 'operator: 'owner',
# 'owner: 'owner'}
next_level = next_levels[level]
return instance.has_level(user, next_level)
@classmethod if new_template_user is not None and len(new_template_user) > 0:
def get_allowed_groups(cls, user): if new_template_user in current_users_groups:
if user.has_perm('dashboard.use_autocomplete'): self.__handle_group_assignment(request)
return Group.objects.all() else:
else: self.__handle_user_assignment(request)
profiles = GroupProfile.get_objects_with_level('owner', user)
return Group.objects.filter(groupprofile__in=profiles).distinct()
@classmethod def __get_removes(self, request):
def get_allowed_users(cls, user): removes = {
if user.has_perm('dashboard.use_autocomplete'): "u": [],
return User.objects.all() "g": [],
else: }
groups = cls.get_allowed_groups(user)
return User.objects.filter( for key, value in request.POST.items():
Q(groups__in=groups) | Q(pk=user.pk)).distinct() m = re.match('remove-([ug])-(.+)', key)
def check_auth(self, whom, old_level, new_level):
if isinstance(whom, Group):
if (not self.is_owner and whom not in
AclUpdateView.get_allowed_groups(self.request.user)):
return False
elif isinstance(whom, User):
if (not self.is_owner and whom not in
AclUpdateView.get_allowed_users(self.request.user)):
return False
return (
AclUpdateView.has_next_level(self.request.user,
self.instance, new_level) and
AclUpdateView.has_next_level(self.request.user,
self.instance, old_level))
def set_level(self, whom, new_level):
user = self.request.user
old_level = self.get_level(whom)
if old_level == new_level:
return
if getattr(self.instance, "owner", None) == whom:
logger.info("Tried to set owner's acl level for %s by %s.",
unicode(self.instance), unicode(user))
msg = _("The original owner cannot be removed, however "
"you can transfer ownership.")
if not getattr(self, 'hide_messages', False):
messages.warning(self.request, msg)
elif self.check_auth(whom, old_level, new_level):
logger.info(
u"Set %s's acl level for %s to %s by %s.", unicode(whom),
unicode(self.instance), new_level, unicode(user))
if not getattr(self, 'hide_messages', False):
self.send_success_message(whom, old_level, new_level)
self.instance.set_level(whom, new_level)
else:
logger.warning(
u"Tried to set %s's acl_level for %s (%s->%s) by %s.",
unicode(whom), unicode(self.instance), old_level, new_level,
unicode(user))
def set_or_remove_levels(self):
for key, value in self.request.POST.items():
m = re.match('(perm|remove)-([ug])-(\d+)', key)
if m: if m:
cmd, typ, id = m.groups() t, name = m.groups()
if cmd == 'remove': removes[t].append(name)
value = None
entity = {'u': User, 'g': Group}[typ].objects.get(id=id) return removes
self.set_level(entity, value)
def __remove_user(self, request, member_id):
def add_levels(self): template = self.get_object()
name = self.request.POST.get('name', None) glance = self.__get_glance_admin_client(request.user.tenant_id)
level = self.request.POST.get('level', None) glance.image_members.delete(template.image_id, member_id)
if not name or not level:
return def __handle_removes(self, request):
try: removes = self.__get_removes(request)
entity = search_user(name) for member_id in removes['u']:
if self.instance.object_level_set.filter(users__in=[entity]): self.__remove_user(request, member_id)
messages.warning( messages.success(request, _("Successfully removed user"))
self.request, _('User "%s" has already '
'access to this object.') % name)
return
except User.DoesNotExist:
entity = None
try:
entity = Group.objects.get(name=name)
if self.instance.object_level_set.filter(groups__in=[entity]):
messages.warning(
self.request, _('Group "%s" has already '
'access to this object.') % name)
return
except Group.DoesNotExist:
messages.warning(
self.request, _('User or group "%s" not found.') % name)
return
self.set_level(entity, level)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.instance = self.get_object() template = self.get_object()
self.is_owner = self.instance.has_level(request.user, 'owner')
self.acl_data = (self.instance.get_users_with_level() + openstack_api.glance.image_update(request, template.image_id, visibility="shared")
self.instance.get_groups_with_level()) self.__handle_assignments(request)
self.set_or_remove_levels() self.__handle_removes(request)
self.add_levels()
return redirect("%s#access" % self.instance.get_absolute_url()) return redirect("%s#access" % template.get_absolute_url())
class GraphMixin(object): class GraphMixin(object):
......
...@@ -593,7 +593,7 @@ def group_delete(request, group_id): ...@@ -593,7 +593,7 @@ def group_delete(request, group_id):
def group_list(request, domain=None, project=None, user=None, filters=None): def group_list(request, domain=None, project=None, user=None, filters=None):
manager = keystoneclient(request, admin=True).groups manager = keystoneclient(request).groups # TODO: was admin API
groups = [] groups = []
kwargs = { kwargs = {
"domain": domain, "domain": domain,
...@@ -834,7 +834,7 @@ def remove_tenant_user(request, project=None, user=None, domain=None): ...@@ -834,7 +834,7 @@ def remove_tenant_user(request, project=None, user=None, domain=None):
def roles_for_group(request, group, domain=None, project=None): def roles_for_group(request, group, domain=None, project=None):
manager = keystoneclient(request, admin=True).roles manager = keystoneclient(request).roles
return manager.list(group=group, domain=domain, project=project) return manager.list(group=group, domain=domain, project=project)
......
...@@ -119,6 +119,15 @@ class VirtualMachineDescModel(BaseResourceConfigModel): ...@@ -119,6 +119,15 @@ class VirtualMachineDescModel(BaseResourceConfigModel):
abstract = True abstract = True
class TemplateMemberGroup(Model):
group_id = CharField(blank=False, max_length=100, unique=True)
class Meta:
app_label = 'vm'
db_table = 'vm_template_member_group'
class InstanceTemplate(TimeStampedModel): class InstanceTemplate(TimeStampedModel):
"""Virtual machine template. """Virtual machine template.
""" """
......
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