def is_manageable_by_reseller(user, db_image: Image, message): reseller_resources = user_reseller_resources(user=user) manageable = False try: if db_image.reseller_resources == reseller_resources: manageable = True if (not manageable and db_image.project.service and (db_image.project.service.client.reseller_resources and db_image.project.service.client.reseller_resources == reseller_resources)): manageable = True if (not manageable and 'image_type' in db_image.properties and db_image.properties['image_type'] == 'snapshot'): # if snapshot, first check if related instance is related to the reseller resources if 'instance_uuid' in db_image.properties: related_instance = Instance.objects.filter( id=db_image.properties['instance_uuid']).first() if related_instance: if related_instance.project.service.client.reseller_resources == reseller_resources: manageable = True except Exception: raise ForbiddenException( _('Could not determine if image is related to reseller service.' )) if not manageable: raise ForbiddenException(message)
def dissociate_user(self, request, pk): del pk # unused if reseller_active_features.is_enabled('demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode')) client = self.get_object() try: # delete the cart related to client client_cart = client.fleio_cart client_cart.delete() except Client.fleio_cart.RelatedObjectDoesNotExist: pass serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user_model = get_user_model() try: user = user_model.objects.get( id=serializer.validated_data['user_id']) try: # delete the cart related to user user_cart = user.fleio_cart user_cart.delete() except user_model.fleio_cart.RelatedObjectDoesNotExist: pass except user_model.DoesNotExist: raise ObjectNotFound({'detail': _('User not found')}) UserToClient.objects.filter(user=user, client=client).delete() return Response({'detail': _('User dissociated')})
def destroy(self, request, pk=None): """Delete an OpenStack user""" if staff_active_features.is_enabled('demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode')) try: project_ids = [ project.project_id for project in self.projects_queryset() ] os_user = self.os_user_api.get_user(user=pk) if getattr(os_user, 'default_project_id', None) not in project_ids: LOG.error( 'Could not delete user with id {pk}, because project {project} not found in database.' .format(pk=pk, project=os_user.default_project_id)) raise ValidationError({ 'detail': _('Unable to delete the user from OpenStack. Check logs for more info' ) }) self.os_user_api.delete_user(user=pk) except (ClientException, IndexError) as e: LOG.error('Could not delete user with id {pk}, reason: {0}'.format( e, pk=pk)) raise ValidationError({ 'detail': _('Unable to delete the user from OpenStack. Check logs for more info' ) }) else: return Response({'detail': _('User deleted')}, status=HTTP_204_NO_CONTENT)
def token(self, request, pk): del request, pk # unused user = self.get_object() if not user.is_active: raise ForbiddenException(detail=_('User account is inactive')) token, created = Token.objects.get_or_create(user=user) return Response({'token': token.key})
def perform_destroy(self, instance: AppUser): if (instance.is_superuser or instance.username == 'demo') and staff_active_features.is_enabled('demo'): # don't allow deletion of superuser or enduser demo account in demo mode raise ForbiddenException( detail=_('Operation not allowed in demo mode')) if instance.is_superuser: if self.request.user.username == instance.username or self.request.user.is_superuser is False: raise PermissionDenied pk = instance.id if instance.username == self.request.user.username: raise APIBadRequest( _('Cannot delete the user you are logged in with.')) with transaction.atomic(): if instance.permissions: instance.permissions.delete() instance.delete() user = self.request.user staff_delete_user.send(sender=__name__, user=user, user_id=user.id, deleted_user_name=instance.username, deleted_user_id=pk, username=user.username, request=self.request)
def create_reseller_service(self, request, pk): del pk # unused # TODO - #1019: implement proper creation of service here serializer = StaffCreateServiceSerializer(data=request.data) serializer.is_valid(raise_exception=True) client = self.get_object() reseller_resources = client_reseller_resources(client=client) if reseller_resources is not None: raise ForbiddenException( {'detail': _('Client already has a reseller service')}) reseller_product = Product.objects.get( id=serializer.validated_data['product_id']) reseller_product_cycle = reseller_product.cycles.filter( id=serializer.validated_data['product_cycle_id'])[0] service = Service.objects.create( client=client, product=reseller_product, cycle=reseller_product_cycle, display_name='Reseller service', status=ServiceStatus.active, ) module_factory.get_module_instance(service=service).create( service=service) return Response({'detail': _('Ok')})
def perform_create(self, serializer): serializer.is_valid(raise_exception=True) new_user_is_superuser = serializer.validated_data.get('is_superuser') if new_user_is_superuser and staff_active_features.is_enabled('demo'): # don't allow creation of superusers in demo mode raise ForbiddenException( detail=_('Operation not allowed in demo mode')) # Don't allow users to create super users if new_user_is_superuser is True and self.request.user.is_superuser is False: raise PermissionDenied serializer.save()
def update_frontend(self, request, *args, **kwargs): if staff_active_features.is_enabled('demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode')) del request, args, kwargs # unused try: index_manager = IndexManager() index_manager.update_frontend() except IndexManager.IndexUpdateException as e: raise APIBadRequest(detail=e.detail) return Response()
def perform_destroy(self, db_image): """Delete the image.""" if staff_active_features.is_enabled('demo'): raise ForbiddenException(detail=_('Operation not allowed in demo mode')) img = self.get_image() if img.db_image.protected: raise APIConflict(_("Can't delete protected image")) try: img.delete() user = self.request.user staff_delete_image.send(sender=__name__, user=user, user_id=user.id, image_name=img.db_image.name, image_id=img.db_image.id, username=user.username, request=self.request) except Exception as e: LOG.error("Cannot delete image, reason: {}".format(repr(e))) handle(self.request)
def delete_project(self, request, pk): del pk # unused if staff_active_features.is_enabled('demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode')) delete_all_resources = request.data.get('delete_all_resources', False) instance = self.get_object() if delete_all_resources: tasks.delete_client_project_resources.delay( project_id=instance.project_id, mark_project_as_deleted=False) return Response(status=200, data={'details': _('Project delete scheduled')}) else: project = OpenstackProject.with_admin_session(instance.project_id) project.delete() return Response(status=200, data={'details': _('Project deleted')})
def perform_destroy(self, instance): if instance.is_default: raise ForbiddenException( {'detail': 'Cannot delete the default currency'}) try: instance.delete() except ProtectedError as e: raise APIBadRequest( detail={ 'error_type': 'ProtectedError', 'detail': e, 'user_friendly_message': _('Currency cannot be deleted because it is used by other objects' ) }) except IntegrityError as e: raise APIBadRequest(detail=e.args[1])
def download(self, request, pk): del pk, request # unused if not staff_active_features.is_enabled('openstack.images.download'): raise ForbiddenException(_('Image download not allowed')) db_image = self.get_object() # type: OpenstackImage try: images_api = Images(api_session=self.os_admin_api.session) image_data = images_api.download(image=db_image) except (Exception, HTTPNotFound) as e: if type(e) == HTTPNotFound: raise ObjectNotFound(detail=e.details) LOG.exception(e) handle(self.request) else: response = StreamingHttpResponse(streaming_content=image_data) response['Content-Type'] = 'application/octet-stream' response['Content-Disposition'] = 'attachment; filename="{}"'.format(db_image.name) return response
def save_custom_code(self, request, *args, **kwargs): if staff_active_features.is_enabled('demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode')) del args, kwargs # unused custom_code = request.data.get('custom_code', None) if custom_code: for insertion_point in custom_code: custom_code_id = custom_code[insertion_point]['data'].get( 'id', None) instance = CustomCode.objects.filter( id=custom_code_id).first() if custom_code_id else None serializer = CustomCodeSerializer( data=custom_code[insertion_point]['data'], instance=instance) if serializer.is_valid(raise_exception=True): serializer.save() return Response()
def perform_destroy(self, db_flavor): """Delete flavor from nova and mark as deleted in Fleio db.""" if staff_active_features.is_enabled('demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode')) flavor_api = Flavors(api_session=self.identity_admin_api.session) flavor = flavor_api.get(flavor=db_flavor) try: pk = db_flavor.id flavor.delete() user = self.request.user staff_delete_flavor.send(sender=__name__, user=user, user_id=user.id, flavor_name=db_flavor.name, flavor_id=pk, username=user.username, request=self.request) except Exception as e: LOG.error(e) handle(self.request, message=e)
def update_frontend(self): if staff_active_features.is_enabled('demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode')) if not self.is_local_frontend(): raise IndexManager.IndexUpdateException( detail=_('Cannot find frontend instalation')) self.update_vanilla_indexes() if not self.has_vanilla_indexes(): raise IndexManager.IndexUpdateException( detail=_('No unmodified index.html file available')) self.generate_updates_indexes() if not self.has_updated_indexes(): raise IndexManager.IndexUpdateException( detail=_('Failed to generate updated index.html file')) self.update_local_frontend()
def allowed_to_update(user): if user.is_superuser is True and staff_active_features.is_enabled( 'demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode'))
def perform_update(self, serializer): if staff_active_features.is_enabled('demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode')) user = serializer.instance request_user = self.request.user updated_user_fields = get_user_changed_values( user, serializer.validated_data) log_text = format_for_log(updated_user_fields) password_changed = False if 'password' in serializer.validated_data: password_changed = True # Don't allow users to edit super user if user.is_superuser: if request_user.is_superuser is False: raise PermissionDenied # Change superuser status only if the user who makes the request is also a superuser if 'is_superuser' in serializer.validated_data: if request_user.is_superuser is False: serializer.validated_data.pop('is_superuser', None) else: # if the instance user gets marked as superuser also mark it as staff if serializer.validated_data['is_superuser'] is True: serializer.validated_data['is_staff'] = True # Don't allow superuser to remove it's own superuser status if request_user == user and request_user.is_superuser is True: if 'is_superuser' in serializer.validated_data: if serializer.validated_data['is_superuser'] is False: raise APIBadRequest( detail=_('Cannot remove own superuser status')) # Don't allow updating a regular user to staff if user has clients if serializer.validated_data.get('is_staff', False) and not user.is_staff: if user.clients.count() > 0: raise APIBadRequest(detail=_( 'Cannot have a staff user with clients associated. Dissociate all clients and try again.' ), ) serializer.save() if password_changed and updated_user_fields: if request_user == user: user_update_password.send(sender=__name__, user=request_user, user_id=request_user.pk, username=request_user.username, email=request_user.email, request=self.request) user_update.send(sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, email=request_user.email, request=self.request, updated_data=log_text) else: staff_altered_user_password.send( sender=__name__, user=request_user, user_id=request_user.pk, username=request_user.username, username_changed=user.username, user_changed_id=user.pk, request=self.request) staff_altered_user_data.send(sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, username_changed=user.username, user_changed_id=user.pk, request=self.request, updated_data=log_text) elif password_changed: if request_user == user: user_update_password.send(sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, email=request_user.email, request=self.request) else: staff_altered_user_password.send( sender=__name__, user=request_user, user_id=request_user.pk, username=request_user.username, username_changed=user.username, user_changed_id=user.pk, request=self.request) elif updated_user_fields: if request_user == user: user_update.send(sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, email=request_user.email, request=self.request, updated_data=log_text) else: staff_altered_user_data.send(sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, username_changed=user.username, user_changed_id=user.pk, request=self.request, updated_data=log_text)
def allowed_to_update(user): if user.is_superuser is True or user.is_staff is True or not user.is_reseller: raise ForbiddenException(detail=_('Cannot update own staff or reseller status.'))
def perform_update(self, serializer): if reseller_active_features.is_enabled('demo'): raise ForbiddenException( detail=_('Operation not allowed in demo mode')) user = serializer.instance request_user = self.request.user updated_user_fields = get_user_changed_values( user, serializer.validated_data) log_text = format_for_log(updated_user_fields) password_changed = False if 'password' in serializer.validated_data: password_changed = True reseller_resources = user_reseller_resources(self.request.user) # Do not allow editing of users not assigned to reseller if user != self.request.user and user.reseller_resources != reseller_resources: raise PermissionDenied serializer.save() if password_changed and updated_user_fields: if request_user == user: user_update_password.send( sender=__name__, user=request_user, user_id=request_user.pk, username=request_user.username, email=request_user.email, request=self.request, ) user_update.send( sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, email=request_user.email, request=self.request, updated_data=log_text, ) else: reseller_altered_user_password.send( sender=__name__, user=request_user, user_id=request_user.pk, username=request_user.username, username_changed=user.username, user_changed_id=user.pk, request=self.request, ) reseller_altered_user_data.send( sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, username_changed=user.username, user_changed_id=user.pk, request=self.request, updated_data=log_text, ) elif password_changed: if request_user == user: user_update_password.send( sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, email=request_user.email, request=self.request, ) else: reseller_altered_user_password.send( sender=__name__, user=request_user, user_id=request_user.pk, username=request_user.username, username_changed=user.username, user_changed_id=user.pk, request=self.request, ) elif updated_user_fields: if request_user == user: user_update.send( sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, email=request_user.email, request=self.request, updated_data=log_text, ) else: reseller_altered_user_data.send(sender=__name__, user=request_user, username=request_user.username, user_id=request_user.pk, username_changed=user.username, user_changed_id=user.pk, request=self.request, updated_data=log_text)
def create_openstack_service(self, request, pk): del pk # unused # TODO - #1019: implement proper creation of service here serializer = CreateServiceSerializer(data=request.data) serializer.is_valid(raise_exception=True) client = self.get_object() if client.first_project is not None and client.first_project.deleted is False: raise ForbiddenException( {'detail': _('Client already has a project')}) project_id = serializer.validated_data.get('project_id', 'none') if Client.objects.filter( services__openstack_project__project_id=project_id): raise ForbiddenException( {'detail': _('Project already associated with a client')}) openstack_product = Product.objects.get( id=serializer.validated_data['product_id']) openstack_product_cycle = openstack_product.cycles.filter( id=serializer.validated_data['product_cycle_id'])[0] service_external_id = serializer.validated_data.get( 'service_external_id', None) with transaction.atomic(): if serializer.validated_data['create_new_project']: try: ProjectModel.objects.create_project( client=client, openstack_product=openstack_product, openstack_product_cycle=openstack_product_cycle, service_external_id=service_external_id, ) except Conflict: return Response( status=409, data={ 'detail': _('A project already exists for this client') }) except RoleDoesNotExist: # TODO: going to the exact settings page and field isn't implemented yet in frontend # on implementation in frontend change section and configuration_id if necessary # (Issue: https://git.fleio.org/fleio/fleio/issues/1922) if plugin_settings.default_role: error_message = _( 'Role "{}" does not exist in OpenStack. Set an existing role as default ' 'role.').format(plugin_settings.default_role) else: error_message = _( 'OpenStack role was not set in OpenStack settings -> defaults tab. ' 'Set an existing role as default role.') raise ConfigException(message=error_message, section='openstack_plugin_defaults', configuration_id='default_role') else: try: project = IdentityAdminApi( request_session=request.session).client.projects.get( project_id) except NotFound: raise ObjectNotFound({'detail': _('Project not found')}) except ConnectFailure: raise ServiceUnavailable( {'detail': _('Could not connect to openstack')}) with transaction.atomic(): try: ProjectModel.objects.create_project( client=client, openstack_product=openstack_product, openstack_product_cycle=openstack_product_cycle, service_external_id=service_external_id, project_id=project.id, project_domain_id=project.domain_id, disabled=not project.enabled, extras={ 'name': project.name, 'description': project.description, 'is_domain': project.is_domain }).save() except RoleDoesNotExist: # TODO: going to the exact settings page and field isn't implemented yet in frontend # on implementation in frontend change section and configuration_id if necessary # (Issue: https://git.fleio.org/fleio/fleio/issues/1922) if plugin_settings.default_role: error_message = _( 'Role "{}" does not exist in OpenStack. Set an existing role as default ' 'role.').format(plugin_settings.default_role) else: error_message = _( 'OpenStack role was not set in OpenStack settings -> defaults tab. ' 'Set an existing role as default role.') raise ConfigException( message=error_message, section='openstack_plugin_defaults', configuration_id='default_role') return Response({'detail': _('Ok')})