diff --git a/openstack_dashboard/conf/glance_policy.json b/openstack_dashboard/conf/glance_policy.json new file mode 100644 index 0000000000..c345d87f24 --- /dev/null +++ b/openstack_dashboard/conf/glance_policy.json @@ -0,0 +1,33 @@ +{ + "context_is_admin": "role:admin", + "admin_or_owner": "is_admin:True or project_id:%(project_id)s", + "default": "rule:admin_or_owner", + + "add_image": "", + "delete_image": "rule:admin_or_owner", + "get_image": "", + "get_images": "", + "modify_image": "rule:admin_or_owner", + "publicize_image": "", + "copy_from": "", + + "download_image": "", + "upload_image": "", + + "delete_image_location": "", + "get_image_location": "", + "set_image_location": "", + + "add_member": "", + "delete_member": "", + "get_member": "", + "get_members": "", + "modify_member": "", + + "manage_image_cache": "role:admin", + + "get_task": "", + "get_tasks": "", + "add_task": "", + "modify_task": "" +} diff --git a/openstack_dashboard/dashboards/project/images_and_snapshots/images/forms.py b/openstack_dashboard/dashboards/project/images_and_snapshots/images/forms.py index f8f900c3d4..20ce3f38a6 100644 --- a/openstack_dashboard/dashboards/project/images_and_snapshots/images/forms.py +++ b/openstack_dashboard/dashboards/project/images_and_snapshots/images/forms.py @@ -31,6 +31,7 @@ from horizon import forms from horizon import messages from openstack_dashboard import api +from openstack_dashboard import policy IMAGE_BACKEND_SETTINGS = getattr(settings, 'OPENSTACK_IMAGE_BACKEND', {}) @@ -45,6 +46,7 @@ class CreateImageForm(forms.SelfHandlingForm): source_type = forms.ChoiceField( label=_('Image Source'), + required=False, choices=[('url', _('Image Location')), ('file', _('Image File'))], widget=forms.Select(attrs={ @@ -91,10 +93,15 @@ class CreateImageForm(forms.SelfHandlingForm): is_public = forms.BooleanField(label=_("Public"), required=False) protected = forms.BooleanField(label=_("Protected"), required=False) - def __init__(self, *args, **kwargs): - super(CreateImageForm, self).__init__(*args, **kwargs) - if not settings.HORIZON_IMAGES_ALLOW_UPLOAD: + def __init__(self, request, *args, **kwargs): + super(CreateImageForm, self).__init__(request, *args, **kwargs) + if (not settings.HORIZON_IMAGES_ALLOW_UPLOAD or + not policy.check((("image", "upload_image"),), request)): self._hide_file_source_type() + if not policy.check((("image", "set_image_location"),), request): + self._hide_url_source_type() + if not policy.check((("image", "publicize_image"),), request): + self._hide_is_public() self.fields['disk_format'].choices = IMAGE_FORMAT_CHOICES def _hide_file_source_type(self): @@ -105,17 +112,30 @@ class CreateImageForm(forms.SelfHandlingForm): if len(source_type.choices) == 1: source_type.widget = HiddenInput() + def _hide_url_source_type(self): + self.fields['copy_from'].widget = HiddenInput() + source_type = self.fields['source_type'] + source_type.choices = [choice for choice in source_type.choices + if choice[0] != 'url'] + if len(source_type.choices) == 1: + source_type.widget = HiddenInput() + + def _hide_is_public(self): + self.fields['is_public'].widget = HiddenInput() + self.fields['is_public'].initial = False + def clean(self): data = super(CreateImageForm, self).clean() # The image_file key can be missing based on particular upload # conditions. Code defensively for it here... image_file = data.get('image_file', None) + image_url = data.get('copy_from', None) - if not data['copy_from'] and not image_file: + if not image_url and not image_file: raise ValidationError( _("A image or external image location must be specified.")) - elif data['copy_from'] and image_file: + elif image_url and image_file: raise ValidationError( _("Can not specify both image and external image location.")) else: @@ -146,6 +166,7 @@ class CreateImageForm(forms.SelfHandlingForm): if data['architecture']: meta['properties']['architecture'] = data['architecture'] if (settings.HORIZON_IMAGES_ALLOW_UPLOAD and + policy.check((("image", "upload_image"),), request) and data.get('image_file', None)): meta['data'] = self.files['image_file'] else: @@ -188,6 +209,12 @@ class UpdateImageForm(forms.SelfHandlingForm): public = forms.BooleanField(label=_("Public"), required=False) protected = forms.BooleanField(label=_("Protected"), required=False) + def __init__(self, request, *args, **kwargs): + super(UpdateImageForm, self).__init__(request, *args, **kwargs) + if not policy.check((("image", "publicize_image"),), request): + self.fields['public'].widget = forms.CheckboxInput( + attrs={'readonly': 'readonly'}) + def handle(self, request, data): image_id = data['image_id'] error_updating = _('Unable to update image "%s".') diff --git a/openstack_dashboard/dashboards/project/images_and_snapshots/images/tables.py b/openstack_dashboard/dashboards/project/images_and_snapshots/images/tables.py index 5e50cdc51e..9ec01b561b 100644 --- a/openstack_dashboard/dashboards/project/images_and_snapshots/images/tables.py +++ b/openstack_dashboard/dashboards/project/images_and_snapshots/images/tables.py @@ -36,6 +36,7 @@ class LaunchImage(tables.LinkAction): verbose_name = _("Launch") url = "horizon:project:instances:launch" classes = ("btn-launch", "ajax-modal") + policy_rules = (("compute", "compute:create"),) def get_link_url(self, datum): base_url = reverse(self.url) @@ -58,6 +59,7 @@ class LaunchImage(tables.LinkAction): class DeleteImage(tables.DeleteAction): data_type_singular = _("Image") data_type_plural = _("Images") + policy_rules = (("image", "delete_image"),) def allowed(self, request, image=None): # Protected images can not be deleted. @@ -77,6 +79,7 @@ class CreateImage(tables.LinkAction): verbose_name = _("Create Image") url = "horizon:project:images_and_snapshots:images:create" classes = ("ajax-modal", "btn-create") + policy_rules = (("image", "add_image"),) class EditImage(tables.LinkAction): @@ -84,6 +87,7 @@ class EditImage(tables.LinkAction): verbose_name = _("Edit") url = "horizon:project:images_and_snapshots:images:update" classes = ("ajax-modal", "btn-edit") + policy_rules = (("image", "modify_image"),) def allowed(self, request, image=None): if image: @@ -99,6 +103,7 @@ class CreateVolumeFromImage(tables.LinkAction): verbose_name = _("Create Volume") url = "horizon:project:volumes:create" classes = ("ajax-modal", "btn-camera") + policy_rules = (("volume", "volume:create"),) def get_link_url(self, datum): base_url = reverse(self.url) diff --git a/openstack_dashboard/settings.py b/openstack_dashboard/settings.py index f66059d2b9..f0923d5916 100644 --- a/openstack_dashboard/settings.py +++ b/openstack_dashboard/settings.py @@ -209,7 +209,8 @@ POLICY_FILES_PATH = os.path.join(ROOT_PATH, "conf") POLICY_FILES = { 'identity': 'keystone_policy.json', 'compute': 'nova_policy.json', - 'volume': 'cinder_policy.json' + 'volume': 'cinder_policy.json', + 'image': 'glance_policy.json', } SECRET_KEY = None