def get_create_options(self, client, region_name, show_in_fleio=None, requested_image=None, requested_volume=None, include_community_images=False, include_shared_images=False, is_staff=True): return super(StaffInstanceViewSet, self).get_create_options( client=client, region_name=region_name, show_in_fleio=show_in_fleio, requested_image=requested_image, requested_volume=requested_volume, include_community_images=staff_active_features.is_enabled('openstack.images.showcommunity'), include_shared_images=staff_active_features.is_enabled('openstack.images.showshared'), is_staff=is_staff, )
def allowed(self): allowed = copy.deepcopy(INSTANCE_ALLOWED_ACTIONS) if staff_active_features.is_enabled('openstack.floatingips'): allowed[InstanceStatus.ACTIVE] += ['associate_ip', 'dissociate_ip'] allowed[InstanceStatus.STOPPED] += [ 'associate_ip', 'dissociate_ip' ] if staff_active_features.is_enabled( 'openstack.instances.allow_changing_password'): allowed[InstanceStatus.ACTIVE] += [ 'change_password', ] return allowed
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 get_queryset(self): if not staff_active_features.is_enabled('openstack.instances.traffic'): traffic_resource = BillingResource.objects.filter(name='instance_traffic').first() queryset = self.queryset.exclude(resource=traffic_resource).all() return queryset else: return self.queryset
def get_services_statuses(request): celery_active = False if getattr(settings, 'CELERY_TASK_ALWAYS_EAGER', False) is True: celery_active = True else: celery_workers = celery.control.inspect().active( ) # gets celery workers celery_workers_list = celery_workers.keys() if celery_workers else None if celery_workers_list and len(celery_workers_list) > 0: celery_active = True response_dict = OrderedDict([ ('celery', celery_active), ]) # check updated status if staff_active_features.is_enabled('openstack'): with open( getattr(settings, 'UPDATED_LOCK_FILE', '/var/fleio/updated_lock.pid'), 'a+') as fp: try: fcntl.flock(fp.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) except IOError: # file is locked, updated is running updated = True else: # file is not locked, updated is not running fcntl.flock(fp.fileno(), fcntl.F_UNLCK) updated = False response_dict['updated'] = updated return Response(response_dict)
def check_sfa_required_and_get_settings( user: AppUser) -> (bool, Optional[Response]): if user.is_staff: if not staff_active_features.is_enabled( 'clients&users.second_factor_auth'): return False, None else: if not active_features.is_enabled('clients&users.second_factor_auth'): return False, None sfa_methods = SecondFactorAuthMethod.objects.filter(user=user, enabled=True) enabled_sfa_methods = sfa_methods.count() if enabled_sfa_methods: # if user has sfa methods enabled, sfa is required and he'll be able to choose from one of those available_sfa_methods = list() for method in sfa_methods: available_sfa_methods.append( dict( name=method.type.name, display_name=method.type.display_name, default=method.default, help_text=method.type.help_text, )) return True, Response({ 'detail': _('Second factor authentication required'), 'sfa_required': True, 'sfa_methods': available_sfa_methods, }) return False, None
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 get_selected_volume(project, volume): if staff_active_features.is_enabled('openstack.images.shareoncreate'): try: models.Volume.objects.get(pk=volume.pk) except (models.Volume.DoesNotExist, models.Volume.MultipleObjectsReturned): raise exceptions.APIBadRequest(detail=_('Volume not found'), code=404) return StaffInstanceViewSet.get_selected_volume(project, volume)
def dissociate_user(self, request, pk): del pk # unused if staff_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 refresh_plugins(self): definitions = {} # type: Dict[str, PluginDefinition] for app_config in apps.apps.get_app_configs(): # type: apps.AppConfig if hasattr(app_config, 'initialize_plugin') and callable( app_config.initialize_plugin): LOG.info('Found plugin {}'.format(app_config.name)) definition = app_config.initialize_plugin( ) # type: PluginDefinition definition.refresh_components() definitions[definition.app_label] = definition self.found_plugin_definitions = dict(definitions) plugins_in_db = [] # update database for plugin in Plugin.objects.all(): if plugin.app_label in definitions: plugins_in_db.append(plugin.app_label) if not plugin.app_loaded: plugin.app_loaded = True plugin.save(update_fields=['app_loaded']) # remove disabled definitions if not plugin.enabled: del definitions[plugin.app_label] else: definitions[plugin.app_label].plugin_model = plugin else: if plugin.app_loaded: plugin.app_loaded = False plugin.save(update_fields=['app_loaded']) for app_label in definitions: if app_label not in plugins_in_db: plugin_definition = definitions[ app_label] # type: PluginDefinition plugin_definition.plugin_model = Plugin.objects.create( display_name=plugin_definition.display_name, app_name=plugin_definition.app_name, app_label=plugin_definition.app_label, feature_name=plugin_definition.feature_name, staff_feature_name=plugin_definition.staff_feature_name, app_loaded=True) for definition in definitions.values(): if active_features.is_enabled(definition.feature_name): LOG.info('Plugin {} is enabled for enduser'.format( definition.app_label)) self.enduser_active_plugin_definitions[ definition.app_label] = definition if staff_active_features.is_enabled(definition.staff_feature_name): LOG.info('Plugin {} is enabled for staff'.format( definition.app_label)) self.staff_active_plugin_definitions[ definition.app_label] = definition self.active_plugin_definitions = definitions
def get_queryset(self): if self.action == 'list': qs = NotificationTemplate.objects.filter( language=getattr(settings, 'DEFAULT_NOTIFICATION_TEMPLATE_LANGUAGE_CODE')) else: qs = NotificationTemplate.objects.all() if not staff_active_features.is_enabled('openstack'): qs = qs.exclude(category__name='openstack') return qs
def get_selected_image(project, image): """Validate and serialize selected image for create options""" if staff_active_features.is_enabled('openstack.images.shareoncreate'): try: models.Image.objects.get(pk=image.pk) except (models.Image.DoesNotExist, models.Image.MultipleObjectsReturned): raise exceptions.APIBadRequest(detail=_('Image not found'), code=404) return images_serializers.ImageSerializer(initial=image).to_representation(image) else: StaffInstanceViewSet.get_selected_image(project, image)
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 get_instance_backups_and_backup_schedules(self, request, id): if not staff_active_features.is_enabled('openstack.osbackup'): raise APIBadRequest(_('Backup feature is disabled.')) del request # unused instance = self.get_instance() backup_schedules_qs = OpenStackBackupSchedule.objects.filter(instance__id=instance.uuid) backups = models.Image.objects.filter(type='backup', instance_uuid=instance.uuid) return Response({ 'schedules': BackupScheduleSerializer(instance=backup_schedules_qs, many=True).data, 'backups': images_serializers.ImageBriefSerializer(instance=backups, many=True).data, })
def handle(self, *args, **options): if len(sys.argv) > 1 and sys.argv[1] in ['update_frontend']: del sys.argv[1] if staff_active_features.is_enabled('demo'): self.stdout.write('Frontend cannot be updated in demo mode!') return self.stdout.write('Updating frontend ...') index_manager = IndexManager() index_manager.update_frontend() self.stdout.write('Fronted updated.')
def get_app_status(request): if staff_active_features.is_enabled('openstack'): updated_info = AppStatus.objects.filter( status_type=StatusTypesMap.updated_messages_count).first() else: updated_info = None if updated_info: return Response({ 'updated_info_details': updated_info.details_as_dict, 'updated_info_last_updated': updated_info.last_updated, }) return Response(dict())
def update(self, request, pk): serializer_context = {'request': request} serializer = StaffUpdateUserProfileSerializer( data=request.data, context=serializer_context) serializer.is_valid(raise_exception=True) user = request.user # type: AppUser updated_user_fields = utils.get_user_changed_values( user, serializer.validated_data) log_text = utils.format_for_log(updated_user_fields) password = serializer.validated_data.get('password', None) old_password = serializer.validated_data.get('old_password', None) user.first_name = serializer.validated_data['first_name'] user.last_name = serializer.validated_data['last_name'] user.email = serializer.validated_data['email'] language = serializer.validated_data.get('language') if language: user.language = language if 'mobile_phone_number' in serializer.validated_data: user.mobile_phone_number = serializer.validated_data.get( 'mobile_phone_number') self.allowed_to_update(user) password_changed = False if password and not staff_active_features.is_enabled('demo'): if user.check_password(old_password): user.set_password(password) utils.login_without_password(request, user) password_changed = True else: raise APIBadRequest( _('Please enter the correct current password.')) user.save() if password_changed: user_update_password.send(sender=__name__, user=user, username=user.username, user_id=user.pk, email=user.email, request=request) if updated_user_fields: user_update.send(sender=__name__, user=user, username=user.username, user_id=user.pk, email=user.email, request=request, updated_data=log_text) return Response({'detail': _('User profile updated')})
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 handle(self, *args, **options): if options['type'] == 'staff': sys.exit(0 if staff_active_features.is_enabled(options['feature'] ) else 1) if options['type'] == 'reseller': sys.exit(0 if reseller_active_features. is_enabled(options['feature']) else 1) if options['type'] == 'enduser': sys.exit( 0 if active_features.is_enabled(options['feature']) else 1) sys.exit(1)
def validate(self, attrs): attrs = super(StaffImageCreateSerializer, self).validate(attrs) if attrs.get( 'source') == 'file' and not staff_active_features.is_enabled( 'openstack.images.file_uploads'): raise serializers.ValidationError( detail={'source': 'File uploads are not allowed.'}) if attrs.get('source') == 'file' and not attrs.get('file'): raise serializers.ValidationError( detail={'source': 'A File is required'}) elif attrs.get('source') == 'url' and not attrs.get('url'): raise serializers.ValidationError( detail={'url': 'A valid URL is required'}) return attrs
def sign_up_settings_view(request): conf = SignUpSettingsConfig(raise_if_required_not_set=False) if request.method == 'GET': return get_sign_up_settings(configuration=conf, with_email_templates=True) elif request.method == 'POST': if staff_active_features.is_enabled('demo'): raise APIBadRequest( _('Cannot change sign up settings in demo mode')) serializer = SignUpSettingsSerializer(instance=conf, data=request.data) serializer.is_valid(raise_exception=True) serializer.save() return get_sign_up_settings(configuration=conf) else: return Response({})
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 send_staff_notification(name: str, priority: str = Notification.PRIORITY_LOW, variables: Optional[Dict[str, str]] = None): if not staff_active_features.is_enabled('notifications.send'): LOG.info('Notification sending is disabled for staff, aborting') return if variables is None: variables = {} for staff_user in AppUser.objects.filter(is_staff=True): send( name=name, priority=priority, user=staff_user, variables=variables, )
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 confirm_login(user, remember=False, **args) -> Optional[str]: """Method called when user supplies second factor authentication info on login Returns either the remember token or None, in either case the user will get logged in Raising exception denies access to user""" if user.is_staff: demo_mode = staff_active_features.is_enabled('demo') else: demo_mode = active_features.is_enabled('demo') if demo_mode: return None # in demo mode, allow any input and sign in the user code = args.get('code') if not code: raise APIBadRequest(_('Confirmation code is missing')) try: int(code) except Exception as e: del e # unused raise APIBadRequest(_('Code has to be a number')) if int(code) > 999999: raise APIBadRequest(_('Code has to be a 6 digit number')) sfa_type = SecondFactorAuthType.objects.filter(name=utils_get_app_name( app_name=SMSAuthenticatorPluginConfig.name)).first() if not sfa_type: raise SFATypeNotFound() sfa_method = SecondFactorAuthMethod.objects.filter(user=user, type=sfa_type).first() if not sfa_method: raise SFAMethodNotAdded() sms_auth_data = SMSAuthenticatorData.objects.filter( method=sfa_method).first() secret_key = sms_auth_data.get_secret_key() result = pyotp.hotp.HOTP(secret_key).verify(otp=code, counter=sms_auth_data.counter) if result: sms_auth_data.counter = sms_auth_data.counter + 1 sms_auth_data.save(update_fields=['counter']) if remember: return RememberSfa(user=user).make_token() return None else: raise APIBadRequest(_('Code is invalid'))
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()