def filter_queryset(self, request, queryset, view): event_types = request.query_params.getlist('event_type') if event_types: queryset = queryset.filter(event_type__in=event_types) features = request.query_params.getlist('feature') if features: queryset = queryset.filter( event_type__in=expand_event_groups(features)) if 'scope' in request.query_params: field = core_serializers.GenericRelatedField( related_models=utils.get_loggable_models()) field._context = {'request': request} scope = field.to_internal_value(request.query_params['scope']) # Check permissions visible = scope._meta.model.get_permitted_objects(request.user) if not visible.filter(pk=scope.pk).exists(): return queryset.none() content_type = ContentType.objects.get_for_model(scope._meta.model) events = models.Feed.objects.filter( content_type=content_type, object_id=scope.id, ).values_list('event_id', flat=True) queryset = queryset.filter(id__in=events) elif not request.user.is_staff and not request.user.is_support: # If user is not staff nor support, he is allowed to see # events related to particular scope only. queryset = queryset.none() return queryset
class AlertSerializer(serializers.HyperlinkedModelSerializer): scope = GenericRelatedField(related_models=utils.get_loggable_models()) severity = MappedChoiceField( choices=[(v, k) for k, v in models.Alert.SeverityChoices.CHOICES], choice_mappings={v: k for k, v in models.Alert.SeverityChoices.CHOICES}, ) context = JsonField(read_only=True) class Meta(object): model = models.Alert fields = ( 'url', 'uuid', 'alert_type', 'message', 'severity', 'scope', 'created', 'closed', 'context', 'acknowledged', ) read_only_fields = ('uuid', 'created', 'closed') extra_kwargs = { 'url': {'lookup_field': 'uuid'}, } def create(self, validated_data): try: alert, created = loggers.AlertLogger().process( severity=validated_data['severity'], message_template=validated_data['message'], scope=validated_data['scope'], alert_type=validated_data['alert_type'], ) except IntegrityError: # In case of simultaneous requests serializer validation can pass for both alerts, # so we need to handle DB IntegrityError separately. raise serializers.ValidationError(_('Alert with given type and scope already exists.')) else: return alert
def get_permitted_objects_uuids(self, user): from waldur_core.logging.utils import get_loggable_models permitted_objects_uuids = {} for model in get_loggable_models(): for field, uuids in model.get_permitted_objects_uuids( user).items(): permitted_objects_uuids[field] = [uuid.hex for uuid in uuids] return permitted_objects_uuids
def setUp(self): from waldur_core.structure.tests.factories import UserFactory self.user = UserFactory(is_staff=True) self.request = APIRequestFactory().get('/') self.request.user = self.user self.field = GenericRelatedField(related_models=get_loggable_models()) self.field.root._context = {'request': self.request}
def ready(self): from waldur_core.logging import handlers, utils for index, model in enumerate(utils.get_loggable_models()): signals.post_delete.connect( handlers.remove_related_alerts, sender=model, dispatch_uid= 'waldur_core.logging.handlers.remove_{}_{}_related_alerts'. format(model.__name__, index), )
def filtered_for_user(self, user, queryset=None): from waldur_core.logging import utils if queryset is None: queryset = self.get_queryset() # XXX: This circular dependency will be removed then filter_queryset_for_user # will be moved to model manager method from waldur_core.structure.managers import filter_queryset_for_user query = Q() for model in utils.get_loggable_models(): user_object_ids = filter_queryset_for_user( model.objects.all(), user).values_list('id', flat=True) content_type_id = ct_models.ContentType.objects.get_for_model( model).id query |= Q(object_id__in=user_object_ids, content_type_id=content_type_id) return queryset.filter(query)
class EventSerializer(serializers.Serializer): level = serializers.ChoiceField( choices=['debug', 'info', 'warning', 'error']) message = serializers.CharField() scope = GenericRelatedField(related_models=utils.get_loggable_models(), required=False)
def filter_queryset(self, request, queryset, view): search_text = request.query_params.get( settings.api_settings.SEARCH_PARAM, '') must_terms = {} must_not_terms = {} should_terms = {} excluded_event_types = set() if 'event_type' in request.query_params: must_terms['event_type'] = request.query_params.getlist( 'event_type') if 'feature' in request.query_params: features = request.query_params.getlist('feature') must_terms['event_type'] = expand_event_groups(features) # Group events by features in order to prevent large HTTP GET request if 'exclude_features' in request.query_params: features = request.query_params.getlist('exclude_features') excluded_event_types.update(expand_event_groups(features)) if 'exclude_extra' in request.query_params: excluded_event_types.update(expand_event_groups(['update'])) if not django_settings.DEBUG: excluded_event_types.update(expand_event_groups(['debug_only'])) if excluded_event_types: must_not_terms['event_type'] = list(excluded_event_types) if 'user_username' in request.query_params: must_terms['user_username'] = [ request.query_params.get('user_username') ] if 'scope' in request.query_params: field = core_serializers.GenericRelatedField( related_models=utils.get_loggable_models()) field._context = {'request': request} obj = field.to_internal_value(request.query_params['scope']) # XXX: Ilja - disabling this hack and re-opening a ticket. Additional analysis is required for # a proper resolution # # XXX: hack to prevent leaking customer events # permitted_uuids = [uuid.hex for uuids in # obj.get_permitted_objects_uuids(request.user).values() for uuid in uuids] # if obj.uuid.hex not in permitted_uuids: # raise ValidationError('You do not have permission to view events for scope %s' # % request.query_params['scope']) for key, val in obj.filter_by_logged_object().items(): must_terms[format_raw_field(key)] = [val] elif 'scope_type' in request.query_params: choices = utils.get_scope_types_mapping() try: scope_type = choices[request.query_params['scope_type']] except KeyError: raise ValidationError( _('Scope type "%(value)s" is not valid. Has to be one from list: %(items)s.' ) % dict(value=request.query_params['scope_type'], items=', '.join(choices.keys()))) else: permitted_items = scope_type.get_permitted_objects_uuids( request.user).items() if not permitted_items: return EmptyQueryset() for field, uuids in permitted_items: must_terms[field] = [uuid.hex for uuid in uuids] elif 'resource_type' in request.query_params and 'resource_uuid' in request.query_params: # Filter events by resource type and uuid. # Please note, that permission checks are skipped, # because we can't check permission for deleted resources. # Also note, that resource type validation is skipped as well, # because resource type name formatting is defined in structure application, # but we don't want to create circular dependency between logging and structure apps. # This issue could be fixed by switching resource type name formatting to str(model._meta) # as it is done for scope_type parameter validation. must_terms[format_raw_field('resource_type')] = [ request.query_params['resource_type'] ] must_terms[format_raw_field('resource_uuid')] = [ request.query_params['resource_uuid'] ] else: should_terms.update( event_logger.get_permitted_objects_uuids(request.user)) mapped = { 'start': request.query_params.get('from'), 'end': request.query_params.get('to'), } timestamp_interval_serializer = core_serializers.TimestampIntervalSerializer( data={k: v for k, v in mapped.items() if v}) timestamp_interval_serializer.is_valid(raise_exception=True) filter_data = timestamp_interval_serializer.get_filter_data() queryset = queryset.filter(search_text=search_text, should_terms=should_terms, must_terms=must_terms, must_not_terms=must_not_terms, start=filter_data.get('start'), end=filter_data.get('end')) order_by = request.query_params.get('o', '-@timestamp') if order_by: queryset = queryset.order_by(order_by) return queryset
def filter_queryset(self, request, queryset, view): mapped = { 'start': request.query_params.get('from'), 'end': request.query_params.get('to'), } timestamp_interval_serializer = core_serializers.TimestampIntervalSerializer( data={k: v for k, v in mapped.items() if v}) timestamp_interval_serializer.is_valid(raise_exception=True) filter_data = timestamp_interval_serializer.get_filter_data() if 'start' in filter_data: queryset = queryset.filter( Q(closed__gte=filter_data['start']) | Q(closed__isnull=True)) if 'end' in filter_data: queryset = queryset.filter(created__lte=filter_data['end']) if 'opened' in request.query_params: queryset = queryset.filter(closed__isnull=True) if 'closed' in request.query_params: queryset = queryset.filter(closed__isnull=False) if 'severity' in request.query_params: severity_codes = { v: k for k, v in models.Alert.SeverityChoices.CHOICES } severities = [ severity_codes.get(severity_name) for severity_name in request.query_params.getlist('severity') ] queryset = queryset.filter(severity__in=severities) # XXX: this filter is wrong and deprecated, need to be removed after replacement in Portal if 'scope_type' in request.query_params: choices = { camel_case_to_underscore(m.__name__): m for m in utils.get_loggable_models() } try: scope_type = choices[request.query_params['scope_type']] except KeyError: raise ValidationError( _('Scope type "%(value)s" is not valid. Has to be one from list: %(items)s.' ) % dict(value=request.query_params['scope_type'], items=', '.join(choices.keys()))) else: ct = ContentType.objects.get_for_model(scope_type) queryset = queryset.filter(content_type=ct) if 'alert_type' in request.query_params: queryset = queryset.filter( alert_type__in=request.query_params.getlist('alert_type')) # Group alerts by features in order to prevent large HTTP GET request if 'exclude_features' in request.query_params: features = request.query_params.getlist('exclude_features') queryset = queryset.exclude( alert_type__in=expand_alert_groups(features)) return queryset
def get_related_models(self): return utils.get_loggable_models()