def get_snapshots_for_instance(self, request): instance_uuid = request.GET.get('instance_uuid', None) # check if the user requests snapshots for an instance that he owns instance = Instance.objects.filter(id=instance_uuid).first() if not instance: raise APIBadRequest( _('Cannot get snapshots for non existing instance.')) client_project = Project.objects.filter( service__client__in=request.user.clients.all()).first( ) # type: Project if not client_project: raise APIBadRequest(_('Client does not have any project.')) if instance.project.project_id != client_project.project_id: raise ObjectNotFound() volume_attachments = VolumeAttachments.objects.filter( server_id=instance_uuid).values('volume_id') volumes = Volume.objects.filter(id__in=volume_attachments) volume_snapshots_qs = VolumeSnapshot.objects.filter(volume__in=volumes) volume_snapshots_count = volume_snapshots_qs.count() volume_snapshots_uuids = volume_snapshots_qs.values('id') image_snapshots = Image.objects.get_images_for_project( project_id=client_project.project_id).filter( Q(volume_snapshot_uuid__in=volume_snapshots_uuids) | Q(instance_uuid=instance_uuid)) serializer = ImageSerializer(image_snapshots, many=True) return Response({ 'objects': serializer.data, 'volume_snapshots_count': volume_snapshots_count, })
def enable_sfa_method(self, request): verification_code = request.data.get('verification_code') try: int(verification_code) except Exception as e: del e # unused raise APIBadRequest(_('Code has to be a number')) try: sfa_type = self.get_sfa_type() except Exception as e: raise e # sfa_method should already be here added through add_sfa_method action sfa_method = SecondFactorAuthMethod.objects.filter( user=self.request.user, type=sfa_type).first() if not sfa_method: raise SFAMethodNotAdded() if sfa_method.enabled: raise APIBadRequest(_('This method is already enabled')) google_auth_data = self.get_google_auth_data(sfa_method=sfa_method, create=True) secret_key = google_auth_data.get_secret_key() totp_code = pyotp.totp.TOTP(secret_key) allowed_to_enable = totp_code.verify(verification_code) if allowed_to_enable: sfa_method.enabled = True # if no other method is set as default, set this one default_method = SecondFactorAuthMethod.objects.filter( user=self.request.user, default=True).first() if not default_method: sfa_method.default = True sfa_method.save() else: raise APIBadRequest(_('Verification code is invalid.')) return Response({'detail': _('Operation completed')})
def get_groups_available_for_user(self, request): """ Gets user groups where a given user is not associated with them :param request: :return: """ user_id = request.query_params.get('user_id', None) search = request.query_params.get('search', None) if not user_id: raise APIBadRequest(_('Missing user id to get groups for.')) try: user = AppUser.objects.get(id=user_id) except AppUser.DoesNotExist: raise APIBadRequest(_('User with provided id not found.')) if search: qs = UserGroup.objects.filter(name=search) else: qs = UserGroup.objects.all() qs = qs.exclude(users__in=[user]) page = self.paginate_queryset(qs) if page is not None: serializer = UserGroupSerializer(page, many=True) return self.get_paginated_response(serializer.data) serializer = UserGroupSerializer(qs, many=True) return Response(serializer.data)
def send_verification_code(self, request): if not request.user.mobile_phone_number: raise APIBadRequest( _('You need to set your mobile phone number first on your profile.' )) try: provider_class_caller = import_string( 'fleio.core.sms_providers.{}.{}.get_sms_provider_class'.format( sms_authenticator_settings.provider, sms_authenticator_settings.provider)) except ImportError as e: del e # unused raise APIBadRequest(_('Could not find SMS provider.')) provider_class = provider_class_caller() try: sfa_type = self.get_sfa_type() except Exception as e: raise e # sfa_method should already be here added through add_sfa_method action sfa_method = SecondFactorAuthMethod.objects.filter( user=self.request.user, type=sfa_type).first() if not sfa_method: raise SFAMethodNotAdded() sms_auth_data = self.get_sms_auth_data(sfa_method=sfa_method, create=True) secret_key = sms_auth_data.get_secret_key() code = pyotp.hotp.HOTP(secret_key).at(count=sms_auth_data.counter) provider_class.send_sms( phone_number=self.request.user.mobile_phone_number, message=sms_authenticator_settings.message.format(code), subject=sms_authenticator_settings.subject, ) return Response({'detail': _('Verification code was sent.')})
def available_to_client(self, request, pk): del pk # unused image = self.get_object() client_id = request.data.get('client', None) if not client_id: raise APIBadRequest(detail=_('Client ID required')) try: client = Client.objects.get(pk=client_id) except (Client.DoesNotExist, ValueError, TypeError): raise APIBadRequest(detail=_('Client does not exist')) needs_sharing = False sharing_message = None client_first_project = client.first_project # Replace when multiple projects per client are supported # client.services.filter(openstack_project__project_id=image.owner).exists() if client_first_project and client_first_project.project_id != image.owner: # Image.owner is not in Client's project if image.visibility == OpenStackImageVisibility.PRIVATE: # Private images with owner different than Client need sharing needs_sharing = True sharing_message = _('Image is private and owned by another project') elif image.visibility == OpenStackImageVisibility.SHARED: # Shared image with owner different and client not in members need sharing if not image.members.filter(member=client_first_project).exists(): needs_sharing = False sharing_message = _('Image is owned by another project') return Response({'needsSharing': needs_sharing, 'sharingMessage': sharing_message})
def create(self, request, *args, **kwargs): if not request.FILES.get('data'): error_map = { 'FILE_TOO_BIG': _('Could not upload attachment. File size is too large.'), 'NO_SPACE_ON_DISK': _('Could not upload attachment. Not enough free space on destination file system.' ) } if self.request.META.get('FILE_TOO_BIG'): raise APIBadRequest(detail=error_map.get('FILE_TOO_BIG')) elif self.request.META.get('NO_SPACE_ON_DISK'): raise APIBadRequest(detail=error_map.get('NO_SPACE_ON_DISK')) else: raise APIBadRequest(detail='Missing data') attachment_data = request.FILES.get('data').read() params = { 'file_name': request.POST.get('file_name'), 'content_type': request.FILES.get('data').content_type } attachment_storage = AttachmentsStorage.get_attachments_storage() disk_file_name = attachment_storage.create_disk_file_name( params['file_name']) params['disk_file'] = disk_file_name attachment_storage.save_attachment(disk_file_name=disk_file_name, attachment_data=attachment_data) obj = Attachment.objects.create(**params) return Response(data=AttachmentSerializerDetail(obj).data)
def update_image_member_status(self, request, pk): del pk # unused image = self.get_object() member_status = request.data.get('member_status', None) if not member_status or ( member_status != ImageMemberStatus.PENDING and member_status != ImageMemberStatus.ACCEPTED and member_status != ImageMemberStatus.REJECTED): raise APIBadRequest(_('Invalid member status received.')) try: project = request.user.clients.first().first_project except (Exception, AttributeError) as e: del e # unused raise APIBadRequest( _('Could not find related client OpenStack project.')) api_session = IdentityUserApi(project.project_id, project.project_domain_id, cache=request.session).session api_image = APIImage(db_image=image, api_session=api_session) try: api_image.update_member(member_project_id=project.project_id, member_status=member_status) except Exception as e: raise APIBadRequest(str(e)) return Response({'detail': _('Image member status update sent.')})
def perform_update(self, serializer): sfa_type = self.get_object() enabled_to_staff = serializer.validated_data.get('enabled_to_staff', None) enabled_to_enduser = serializer.validated_data.get('enabled_to_enduser', None) no_other_option_msg = _( 'Second factor authentication is required and users have no other option than the one you want to disable.' ) staff_users = AppUser.objects.filter(is_staff=True) if enabled_to_staff is False: # check if other method is available if sfa is required staff_other_types_count = SecondFactorAuthType.objects.filter( enabled_to_staff=True ).exclude(id=sfa_type.id).count() if staff_other_types_count == 0 and (sfa_settings.require_staff_users_to_use_sfa is True or sfa_settings.require_end_users_to_use_sfa is True): raise APIBadRequest(no_other_option_msg) # disable methods only for staff-users SecondFactorAuthMethod.objects.filter(type=sfa_type, user__in=staff_users).update( enabled=False, default=False ) if enabled_to_enduser is False: enduser_other_types_count = SecondFactorAuthType.objects.filter( enabled_to_enduser=True ).exclude(id=sfa_type.id).count() # check if other method is available if sfa is required if enduser_other_types_count == 0 and sfa_settings.require_end_users_to_use_sfa is True: raise APIBadRequest(no_other_option_msg) # disable methods only for end-users SecondFactorAuthMethod.objects.filter( type=sfa_type ).exclude( user__in=staff_users ).update(enabled=False, default=False) return super().perform_update(serializer=serializer)
def create_options(self, request): network_id = request.query_params.get('network_id', None) if not network_id: raise APIBadRequest(_('Network id required in request')) try: network = Network.objects.get(id=network_id) pools = SubnetPool.objects.filter(region=network.region) ipv6_modes = ['slaac', 'dhcpv6-stateful', 'dhcpv6-stateless'] return Response({'pools': SubnetPoolSerializer(instance=pools, many=True).data, 'ipv6_modes': ipv6_modes}) except Network.DoesNotExist: raise APIBadRequest(_('Network not found'))
def reset_state(self, request, pk): del pk # unused os_volume_snapshot = self.get_openstack_volume_snapshot() state = request.data.get('state') if not state: raise APIBadRequest( _('Cannot reset state because new state was not provided.')) try: os_volume_snapshot.reset_state(state=state) except Exception as e: raise APIBadRequest(_(str(e))) return Response({'detail': _('State reset scheduled.')})
def get_applicable_tax_rules(request: Request): client_id = request.query_params.get('client_id', None) if not client_id: raise APIBadRequest(_('No client id provided')) client = Client.objects.filter(id=client_id, users__in=[request.user]).first() if not client: raise APIBadRequest(_('No client found for provided id')) applicable_tax_rules = TaxRule.for_country_and_state(country=client.country_name, state=client.state) if len(applicable_tax_rules) == 0: return Response({}) else: return Response(EndUserTaxRuleSerializer(instance=applicable_tax_rules, many=True).data)
def dissociate_configurable_option(self, request, pk): del pk # unused product = self.get_object() option_id = request.data.get('option', None) if option_id is None: raise APIBadRequest(_('No option provided')) try: option_id = int(option_id) except ValueError: raise APIBadRequest(_('Option does not exist')) if product.configurable_options.filter(id=option_id).exists() == 0: raise APIConflict(_('Option not associated with product')) ProductConfigurableOption.objects.filter( product=product, configurable_option__id=option_id).delete() return Response({'detail': _('Option dissociated')})
def get_available_flavor_groups_for_image(self, request): image_id = request.query_params.get('image_id') search = request.query_params.get('search') if not image_id: raise APIBadRequest(_('Missing image id to filter flavor groups against it')) try: Image.objects.get(id=image_id) except Image.DoesNotExist: raise APIBadRequest(_('No image to filter against')) queryset = FlavorGroup.objects.all() queryset = queryset.exclude(images__id=image_id) if search: queryset = queryset.filter(name__icontains=search) objects = AdminFlavorGroupSerializer(instance=queryset, many=True, read_only=True).data return Response({'objects': objects})
def update_billing_plan(self, request, pk): del pk # unused if not staff_active_features.is_enabled('openstack.plans'): raise APIBadRequest(detail=_('Cannot update os plan because openstack plans feature is disabled')) service = self.get_object() # type: Service if service.status in [ ServiceStatus.terminated, ServiceStatus.canceled, ServiceStatus.fraud ]: raise APIBadRequest(_('Cannot change pricing plan for service in this state.')) new_plan_id = request.data.get('plan') billing_module = module_factory.get_module_instance(service=service) billing_module.change_pricing_plan(service=service, new_plan_id=new_plan_id) return Response({'detail': _('Pricing plan updated')})
def assign_client_group_to_flavor(self, request, pk): """assigns a client group to a flavor""" flavor = self.get_object() client_group_id = request.data.get('client_group', None) if not client_group_id: raise APIBadRequest(_('No client group id specified.')) client_group = ClientGroup.objects.filter(id=client_group_id).first() if not client_group: raise APIBadRequest(_('No client group found for assignment.')) if client_group in flavor.show_to_groups.all(): raise APIBadRequest( _('Client group already assigned to this flavor.')) flavor.show_to_groups.add(client_group) return Response( {'detail': _('Successfully assigned client group to flavor.')})
def process_login_with_sfa(request) -> (bool, Optional[str]): sfa_params = request.data.get('sfa_params') sfa_completed = False cookies = request.stream.COOKIES or {} remember_sfa_token = cookies.get('rSFA') user = auth.authenticate(username=request.data.get('username'), password=request.data.get('password')) if remember_sfa_token and RememberSfa(user=user).check_token( token=remember_sfa_token): sfa_completed = True if sfa_params: del request.data['sfa_params'] sfa_method_name = sfa_params.get('sfa_method_name') remember_sfa = sfa_params.get('rememberSFA', False) try: confirm_method = import_string( 'plugins.{}.common.base_views.confirm_login'.format( sfa_method_name)) except ImportError: LOG.debug('Could not find sfa confirm method for {}'.format( sfa_method_name)) raise APIBadRequest(_('Could not find confirmation method')) sfa_args = sfa_params.get('args') if sfa_args: try: remember_sfa_token = confirm_method(user=user, remember=remember_sfa, **sfa_args) sfa_completed = True except Exception as e: raise e return sfa_completed, remember_sfa_token
def perform_update(self, serializer): db_subnet = self.get_object() serializer.is_valid(raise_exception=True) # do not send allocation pools to openstack if they were not changed existing_allocation_pools = db_subnet.allocation_pools provided_allocation_pools = serializer.validated_data.get( 'allocation_pools') if len(existing_allocation_pools) == len(provided_allocation_pools): allocation_pools_matched = 0 for e_ap in existing_allocation_pools: for p_ap in provided_allocation_pools: if p_ap.get('start') == e_ap.get('start') and p_ap.get( 'end') == e_ap.get('end'): allocation_pools_matched += 1 if allocation_pools_matched == len(existing_allocation_pools): del serializer.validated_data['allocation_pools'] os_api = OSApi.from_request(request=self.request) try: os_api.subnets.update(old_values=serializer.instance, new_values=serializer.validated_data) except BadRequest as e: raise APIBadRequest(force_text(e)) except Conflict as e: raise APIConflict(force_text(e)) except Exception as e: LOG.error(e) handle(self.request, message=_('Unable to update subnet'))
def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) if not validate_cloud_objects_limit(): raise APIBadRequest(_('Licence cloud objects limit reached. Please check your license.')) try: os_image_api = Images(api_session=self.os_admin_api.session) image = os_image_api.create(owner=self.os_admin_api.project_id, **serializer.validated_data) except Exception as e: LOG.error(e) handle(self.request, message='Unable to create the image') else: headers = self.get_success_headers(serializer.data) data = serializer.data data['id'] = image.id reseller_resources = user_reseller_resources(self.request.user) if reseller_resources: Image.objects.update_or_create( defaults={ 'reseller_resources': reseller_resources, 'min_disk': serializer.validated_data.get('min_disk', 0), 'min_ram': serializer.validated_data.get('min_ram', 0), 'region': serializer.validated_data.get('region') }, id=image.id, ) return Response(data, status=status.HTTP_201_CREATED, headers=headers)
def update(self, request, *args, **kwargs): UpdateSerializer = self.get_serializer_class() serializer = UpdateSerializer(data=request.data, context=self.get_serializer_context()) serializer.is_valid(raise_exception=True) plan = self.get_object() default_plans = self.get_queryset().filter( is_default=True, reseller_resources=plan.reseller_resources) if not plan.is_default and serializer.validated_data.get('is_default'): for df_plan in default_plans: df_plan.is_default = False df_plan.save() elif not default_plans.exclude( id=plan.id) and not serializer.validated_data.get( 'is_default'): other_default = serializer.validated_data.get('other_default') if not other_default: raise APIBadRequest(_('There must be a default billing plan')) else: other_default.is_default = True other_default.save() plan.is_default = serializer.validated_data.get('is_default') plan.name = serializer.validated_data['name'] plan.currency = serializer.validated_data['currency'] plan.save() return Response({'plan': self.serializer_class(plan).data})
def save_auto_create_network_options(self, request): if 'region' not in request.data: raise APIBadRequest(_('Must be called with region parameter')) network_admin_api = Networks( api_session=self.identity_admin_api.session) subnetpool_admin_api = SubnetPools( api_session=self.identity_admin_api.session) options = network_admin_api.config_auto_create_network_options( region=request.data['region']) options['config'] = dict() if 'network_id' in request.data: network = network_admin_api.set_network_as_default( request.data['region'], request.data['network_id']) if network: options['config']['network'] = network if 'ipv4_subnetpool' in request.data: ipv4_subnetpool = subnetpool_admin_api.set_subnetpool_as_default( request.data['region'], request.data['ipv4_subnetpool'], ip_version=4) if ipv4_subnetpool: options['config']['ipv4_subnetpool'] = ipv4_subnetpool if 'ipv6_subnetpool' in request.data: ipv6_subnetpool = subnetpool_admin_api.set_subnetpool_as_default( request.data['region'], request.data['ipv6_subnetpool'], ip_version=6) if ipv6_subnetpool: options['config']['ipv6_subnetpool'] = ipv6_subnetpool return Response({'options': options})
def create(self, request): """Create a dns zone""" if not validate_cloud_objects_limit(): raise APIBadRequest(_('Licence cloud objects limit reached. Please check your license.')) serializer = DnsCreateSerializer(data=request.data) serializer.is_valid(raise_exception=True) designate = designate_client(self.identity_admin_api.session, region_name=serializer.validated_data.pop('region_name', None), sudo_project_id=serializer.validated_data.pop('sudo_project_id', None), all_projects=serializer.validated_data.pop('all_projects', None)) serializer.validated_data.pop('client', None) try: zone = designate.zones.create(**serializer.validated_data) except designate_exceptions.Conflict as e: LOG.error('Unable to create zone, reason {0}'.format(e)) raise ValidationError( {'detail': _('Unable to create zone. A zone with the same domain name already exists')} ) except designate_exceptions.Base as e: LOG.error('Unable to create zone, reason {0}'.format(e)) raise ValidationError({'detail': _('Unable to create zone. Please see logs for more info')}) else: return Response(zone, status=HTTP_201_CREATED)
def remove_ip(self, request, pk): del pk # unused port = self.get_port() serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) try: port = port.remove_ip(kwargs=serializer.validated_data) user = self.request.user instance_detach_ips.send( sender=__name__, user=user, user_id=user.id, port_id=port['port']['id'], instance_id=port['port']['device_id'], ips=', '.join([ f_ip['ip_address'] for f_ip in serializer.validated_data['fixed_ips'] ]), network_id=port['port']['network_id'], username=user.username, request=self.request) serializer.validated_data.update(port['port']) except BadRequest as e: raise APIBadRequest(e) except Exception as e: LOG.error(e) handle(self.request, message=_('Unable to remove ip')) return Response({'detail': _('IP removed')})
def get_certificate(self, request, pk): cluster = self.get_object() if cluster.project.project_id != self.get_project_id(): raise APIBadRequest( _('If you want to get certificate for cluster, impersonate the owner.' )) return super().get_certificate(request=request, pk=pk)
def upgrade(self, request, pk): del pk # unused service = self.get_object() if not service.next_invoice_date: raise APIBadRequest( detail=_('Unpaid service cannot be upgraded/downgraded')) ser = ServiceUpgOptionsSerializer(data=request.data, context={'service': service}) ser.is_valid(raise_exception=True) configurable_options = ser.validated_data.get('configurable_options') confirmation = ser.validated_data.get('confirm', False) upgrade_summary = ServiceManager.estimate_new_service_cycle_cost( service=service, product=ser.validated_data['product'], cycle=ser.validated_data['cycle'], start_date=utcnow(), configurable_options=configurable_options) if confirmation: order_metadata = OrderMetadata.from_request(request).to_json() invoice = ServiceManager.create_service_upgrade_order( user=request.user, client=request.user.clients.first(), service=service, product=ser.validated_data['product'], cycle=ser.validated_data['cycle'], start_date=utcnow(), configurable_options=configurable_options, metadata=order_metadata) return Response({'invoice': invoice.pk}) else: return Response(upgrade_summary)
def mark_billing_histories_as_invoiced(self, request): """Method called from external module (fleio-whmcs) to mark billing histories as invoiced""" client_external_billing_id = request.data.get('client_external_billing_id', None) if not client_external_billing_id: raise APIBadRequest(_('Client external billing id is required to fulfill this request.')) try: client = Client.objects.get(external_billing_id=client_external_billing_id) except Client.DoesNotExist: raise APIBadRequest(_('Could not find client related to given external billing id.')) for service in client.services.all(): billing_module = module_factory.get_module_instance(service=service) # this will mark unsettled service dynamic usage histories to invoiced billing_module.get_unsettled_usage(service=service, end_datetime=utcnow()) return Response({ 'detail': _('Successfully marked client {} billing histories states as invoiced.').format(client.id) })
def update(self, name=None, description=None): if APIVersion(plugin_settings.VOLUME_API_VERSION) < APIVersion('3.9'): raise APIBadRequest( _('Volume update is not found in this volume api version')) return self.cinder_api.backups.update(backup=self.volume_backup, description=description, name=name)
def get_active_tos(self, request, *args, **kwargs): del request, args, kwargs # unused latest_version_tos = TermsOfService.objects.filter( draft=False).order_by('-version').first() if not latest_version_tos: return Response({'tos_data': None}) try: tos_agreement = TermsOfServiceAgreement.objects.get_or_create( terms_of_service=latest_version_tos, user=self.request.user, )[0] # type: TermsOfServiceAgreement except Exception as e: raise APIBadRequest(str(e)) remind_later_button_available = True if tos_settings.forbid_access_after: forbid_after_datetime = datetime.datetime.strptime( tos_settings.forbid_access_after, '%Y-%m-%d %H:%M:%S') forbid_after_datetime = forbid_after_datetime.replace( tzinfo=pytz.utc) if utcnow() > forbid_after_datetime: remind_later_button_available = False return Response({ 'tos_data': { 'agreement_id': tos_agreement.id, 'title': tos_agreement.terms_of_service.title, 'version': tos_agreement.terms_of_service.version, 'content': tos_agreement.terms_of_service.content, 'agreed': tos_agreement.agreed, 'remind_later_button_available': remind_later_button_available, } })
def get_subnet_pools(self, request): region = request.query_params.get('region', None) if not region: raise APIBadRequest(_('Region required in request')) pools = SubnetPool.objects.filter(region=region) return Response( {'pools': SubnetPoolSerializer(instance=pools, many=True).data})
def automatic_add_ips(self, request, pk): del pk # unused port = self.get_port() serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) try: openstack_port = port.add_ips( kwargs=serializer.validated_data)['port'] user = self.request.user instance_attach_ips.send( sender=__name__, user=user, user_id=user.id, port_id=openstack_port['id'], instance_id=openstack_port['device_id'], ips=', '.join([ f_ip['ip_address'] for f_ip in openstack_port['fixed_ips'] if f_ip not in port.port.fixed_ips ]), network_id=openstack_port['network_id'], username=user.username, request=self.request) serializer.validated_data.update(openstack_port) except BadRequest as e: raise APIBadRequest(e) except Exception as e: LOG.error(e) handle(self.request, message=_('Unable to add IP(s)')) return Response({'detail': _('IP(s) added')})
def get_member_status(self, request, pk): del pk # unused image = self.get_object() try: project = request.user.clients.first().first_project except (Exception, AttributeError) as e: del e # unused raise APIBadRequest( _('Could not find related client OpenStack project.')) member = ImageMembers.objects.filter(image=image, member=project).first() if not member: raise APIBadRequest( _('Could not find details about image member.')) return Response({'status': member.status})