Exemplo n.º 1
0
    def delete(self, sketch_id, view_id):
        """Handles DELETE request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model
            view_id: Integer primary key for a view database model
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(current_user, 'write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have write access controls on sketch.')
        view = View.query.get(view_id)
        if not view:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No view found with this ID.')

        # Check that this view belongs to the sketch
        if view.sketch_id != sketch.id:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND,
                'The view does not belong to the sketch ({0:d} vs '
                '{1:d})'.format(view.sketch_id, sketch.id))

        if not sketch.has_permission(user=current_user, permission='write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have write permission on sketch.')

        view.set_status(status='deleted')

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return HTTP_STATUS_CODE_OK
Exemplo n.º 2
0
    def delete(self, sketch_id, group_id):
        """Handles DELETE request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model.
            group_id: Integer primary key for an aggregation group database
                model.
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        group = AggregationGroup.query.get(group_id)

        if not group:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No Group found with this ID.')

        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        # Check that this group belongs to the sketch
        if group.sketch_id != sketch.id:
            msg = ('The sketch ID ({0:d}) does not match with the aggregation '
                   'group sketch ID ({1:d})'.format(sketch.id,
                                                    group.sketch_id))
            abort(HTTP_STATUS_CODE_FORBIDDEN, msg)

        if not sketch.has_permission(user=current_user, permission='write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'The user does not have write permission on the sketch.')

        db_session.delete(group)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return HTTP_STATUS_CODE_OK
Exemplo n.º 3
0
    def post(self, sketch_id):
        """Handles POST request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model

        Returns:
            A view in JSON (instance of flask.wrappers.Response)
        """
        form = forms.SaveViewForm.build(request)
        if not form.validate_on_submit():
            abort(HTTP_STATUS_CODE_BAD_REQUEST,
                  'Unable to save view, not able to validate form data.')

        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(current_user, 'write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have write access controls on sketch.')

        view = self.create_view_from_form(sketch, form)

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(view, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 4
0
    def post(self, sketch_id):
        """Handles POST request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model

        Returns:
            An aggregation in JSON (instance of flask.wrappers.Response)
        """
        form = request.json
        if not form:
            form = request.data

        if not form:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST, 'Unable to validate form data.')

        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(user=current_user, permission='write'):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                'The user does not have write permission on the sketch.')

        aggregation = self.create_aggregation_from_form(sketch, form)

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(aggregation, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 5
0
    def get(self, sketch_id, group_id):
        """Handles GET request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model.
            group_id: Integer primary key for an aggregation group database
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        group = AggregationGroup.query.get(group_id)

        if not group:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No Group found with this ID.')

        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(user=current_user, permission='read'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'The user does not have read permission on the sketch.')

        # Check that this group belongs to the sketch
        if group.sketch_id != sketch.id:
            msg = ('The sketch ID ({0:d}) does not match with the aggregation '
                   'group sketch ID ({1:d})'.format(sketch.id,
                                                    group.sketch_id))
            abort(HTTP_STATUS_CODE_FORBIDDEN, msg)

        _, objects, meta = utils.run_aggregator_group(group,
                                                      sketch_id=sketch.id)
        schema = {'meta': meta, 'objects': objects}

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return jsonify(schema)
Exemplo n.º 6
0
    def delete(self, sketch_id, story_id):
        """Handles DELETE request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model
            story_id: Integer primary key for a story database model
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        story = Story.query.get(story_id)

        if not story:
            msg = 'No Story found with this ID.'
            abort(HTTP_STATUS_CODE_NOT_FOUND, msg)

        if not sketch:
            msg = 'No sketch found with this ID.'
            abort(HTTP_STATUS_CODE_NOT_FOUND, msg)

        # Check that this timeline belongs to the sketch
        if story.sketch_id != sketch.id:
            msg = ('The sketch ID ({0:d}) does not match with the story'
                   'sketch ID ({1:d})'.format(sketch.id, story.sketch_id))
            abort(HTTP_STATUS_CODE_FORBIDDEN, msg)

        if not sketch.has_permission(user=current_user, permission='write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'The user does not have write permission on the sketch.')

        sketch.stories.remove(story)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return HTTP_STATUS_CODE_OK
Exemplo n.º 7
0
    def post(self, sketch_id):
        """Handles POST request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model

        Returns:
            A view in JSON (instance of flask.wrappers.Response)
        """
        form = forms.StoryForm.build(request)
        if not form.validate_on_submit():
            abort(HTTP_STATUS_CODE_BAD_REQUEST,
                  'Unable to validate form data.')

        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')
        if not sketch.has_permission(current_user, 'write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have write access controls on sketch.')

        title = ''
        if form.title.data:
            title = form.title.data
        story = Story(title=title,
                      content='[]',
                      sketch=sketch,
                      user=current_user)
        db_session.add(story)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)
        return self.to_json(story, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 8
0
    def get(self, sketch_id):
        """Handles GET request to the resource.

        Handler for /api/v1/sketches/<int:sketch_id>/aggregation/

        Args:
            sketch_id: Integer primary key for a sketch database model

        Returns:
            Views in JSON (instance of flask.wrappers.Response)
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(current_user, 'read'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have read access controls on sketch.')
        aggregations = sketch.get_named_aggregations

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(aggregations)
Exemplo n.º 9
0
    def post(self, sketch_id):
        """Handles POST request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model

        Returns:
            An aggregation in JSON (instance of flask.wrappers.Response)
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(user=current_user, permission='write'):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                'The user does not have write permission on the sketch.')

        form = request.json
        if not form:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'No JSON data, unable to process request to create '
                'a new aggregation group.')

        aggregation_string = form.get('aggregations', '')
        if not aggregation_string:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Unable to create an empty group.')

        agg_list = json.loads(aggregation_string)
        if not isinstance(agg_list, (list, tuple)):
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Aggregations needs to be a list of IDs.')

        named_aggs = sketch.get_named_aggregations
        aggregations = [agg for agg in named_aggs if agg.id in agg_list]

        # Create the aggregation in the database
        aggregation_group = AggregationGroup(
            name=form.get('name', 'No Group Name'),
            description=form.get('description', ''),
            parameters=form.get('parameters', ''),
            aggregations=aggregations,
            orientation=form.get('orientation', 'layer'),
            user=current_user,
            sketch=sketch,
            view=form.get('view_id')
        )
        db_session.add(aggregation_group)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(
            aggregation_group, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 10
0
    def post(self):
        """Handles POST request to the resource.

        Returns:
            A view in JSON (instance of flask.wrappers.Response)
        """
        upload_enabled = current_app.config['UPLOAD_ENABLED']
        if not upload_enabled:
            abort(HTTP_STATUS_CODE_BAD_REQUEST, 'Upload not enabled')

        form = request.get_data(parse_form_data=True)
        if not form:
            form = request.form

        sketch_id = form.get('sketch_id', None)
        if not sketch_id:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Unable to upload data without supplying a '
                'sketch to associated it with.')

        if not isinstance(sketch_id, int):
            sketch_id = int(sketch_id)

        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND,
                'No sketch found with this ID.')

        if sketch.get_status.status == 'archived':
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Unable to upload a file to an archived sketch.')

        if not sketch.has_permission(current_user, 'write'):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                'Unable to upload data to a sketch, user does not have '
                'write access.')

        utils.update_sketch_last_activity(sketch)

        index_name = form.get('index_name', '')
        file_storage = request.files.get('file')
        if file_storage:
            chunk_index_name = form.get('chunk_index_name', uuid.uuid4().hex)
            return self._upload_file(
                file_storage=file_storage, chunk_index_name=chunk_index_name,
                form=form, sketch=sketch, index_name=index_name)

        events = form.get('events')
        if not events:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Unable to upload data, no file uploaded nor any events.')

        return self._upload_events(
            events=events, form=form, sketch=sketch, index_name=index_name)
Exemplo n.º 11
0
    def post(self, sketch_id):
        """Handles POST request to the resource.

        Returns:
            Graph in JSON (instance of flask.wrappers.Response)
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        form = request.json
        plugin_name = form.get('plugin')
        graph_config = form.get('config')
        refresh = form.get('refresh')

        sketch_indices = [
            timeline.searchindex.index_name
            for timeline in sketch.active_timelines
        ]

        cache = GraphCache.get_or_create(
            sketch=sketch, graph_plugin=plugin_name)

        if graph_config:
            cache_config = graph_config
        else:
            cache_config = cache.graph_config

        if isinstance(cache_config, str):
            cache_config = json.loads(cache_config)

        # Refresh cache if timelines have been added/removed from the sketch.
        if cache_config:
            cache_graph_filter = cache_config.get('filter', {})
            cache_filter_indices = cache_graph_filter.get('indices', [])
            if set(sketch_indices) ^ set(cache_filter_indices):
                refresh = True

        if cache.graph_elements and not refresh:
            return self.to_json(cache)

        graph_class = manager.GraphManager.get_graph(plugin_name)
        graph = graph_class(sketch=sketch)
        cytoscape_json = graph.generate().to_cytoscape()

        if cytoscape_json:
            cache.graph_elements = json.dumps(cytoscape_json)
            cache.graph_config = json.dumps(graph_config)
            cache.update_modification_time()
            db_session.add(cache)
            db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(cache)
Exemplo n.º 12
0
    def post(self, sketch_id, graph_id):
        """Handles GET request to the resource.

        Returns:
            List of graphs in JSON (instance of flask.wrappers.Response)
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(current_user, 'write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have write access controls on sketch.')

        graph = Graph.query.get(graph_id)
        if not graph:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No graph found with this ID.')

        if not sketch.id == graph.sketch.id:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Graph does not belong to this sketch.')

        form = request.json
        if not form:
            form = request.data

        name = form.get('name')
        if name:
            graph.name = name

        description = form.get('description')
        if description:
            graph.description = description

        elements = form.get('elements')
        if elements:
            graph.graph_elements = json.dumps(elements)

        graph_config = form.get('graph_config')
        graph_json = ''
        if graph_config:
            if isinstance(graph_config, dict):
                graph_json = json.dumps(graph_config)
            elif isinstance(graph_config, str):
                graph_json = graph_config
        if graph_json:
            graph.graph_config = graph_json

        db_session.add(graph)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(graph, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 13
0
    def post(self, sketch_id, story_id):
        """Handles POST request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model
            story_id: Integer primary key for a story database model

        Returns:
            A view in JSON (instance of flask.wrappers.Response)
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        story = Story.query.get(story_id)

        if not story:
            msg = "No Story found with this ID."
            abort(HTTP_STATUS_CODE_NOT_FOUND, msg)

        if not sketch:
            msg = "No sketch found with this ID."
            abort(HTTP_STATUS_CODE_NOT_FOUND, msg)

        if story.sketch_id != sketch.id:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND,
                "Sketch ID ({0:d}) does not match with the ID in "
                "the story ({1:d})".format(sketch.id, story.sketch_id),
            )

        if not sketch.has_permission(current_user, "write"):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                "User does not have write access controls on sketch.",
            )

        form = request.json
        if not form:
            form = request.data

        if form and form.get("export_format"):
            export_format = form.get("export_format")
            return jsonify(
                story=self._export_story(
                    story=story, sketch_id=sketch_id, export_format=export_format
                )
            )

        story.title = form.get("title", "")
        story.content = form.get("content", "[]")
        db_session.add(story)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(story, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 14
0
    def get(self, sketch_id, graph_id):
        """Handles GET request to the resource.

        Returns:
            List of graphs in JSON (instance of flask.wrappers.Response)
        """
        args = self.parser.parse_args()
        output_format = args.get('format', None)

        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        graph = Graph.query.get(graph_id)
        if not graph:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No graph found with this ID.')

        if not sketch.id == graph.sketch.id:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Graph does not belong to this sketch.')

        # If requested use a format suitable for the CytoscapeJS frontend.
        if output_format == 'cytoscape':
            response = self.to_json(graph).json
            response['objects'][0]['graph_elements'] = graph.graph_elements
            return jsonify(response)

        # Reformat elements to work with networkx python library.
        # TODO: Change frontend to save directed and multigraph attributes.
        graph_elements = json.loads(graph.graph_elements)
        formatted_graph = {
            'data': [],
            'directed': True,
            'multigraph': True,
            'elements': {
                'nodes': [],
                'edges': []
            }
        }
        for element in graph_elements:
            group = element['group']
            element_data = {'data': element['data']}
            formatted_graph['elements'][group].append(element_data)

        response = self.to_json(graph).json
        response['objects'][0]['graph_elements'] = formatted_graph
        response['objects'][0]['graph_config'] = graph.graph_config

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return jsonify(response)
Exemplo n.º 15
0
    def post(self):
        """Handles POST request to the resource.

        Returns:
            A view in JSON (instance of flask.wrappers.Response)
        """
        upload_enabled = current_app.config['UPLOAD_ENABLED']
        if not upload_enabled:
            abort(HTTP_STATUS_CODE_BAD_REQUEST, 'Upload not enabled')

        form = request.get_data(parse_form_data=True)
        if not form:
            form = request.form

        sketch_id = form.get('sketch_id', None)
        if not isinstance(sketch_id, int):
            sketch_id = int(sketch_id)

        sketch = None
        if sketch_id:
            sketch = Sketch.query.get_with_acl(sketch_id)
            if not sketch:
                abort(HTTP_STATUS_CODE_NOT_FOUND,
                      'No sketch found with this ID.')
            if sketch.get_status.status == 'archived':
                abort(HTTP_STATUS_CODE_BAD_REQUEST,
                      'Unable to upload a file to an archived sketch.')

        index_name = form.get('index_name', uuid.uuid4().hex)
        if not isinstance(index_name, six.text_type):
            index_name = codecs.decode(index_name, 'utf-8')

        file_storage = request.files.get('file')

        if file_storage:
            return self._upload_file(file_storage, form, sketch, index_name)

        events = form.get('events')
        if not events:
            abort(HTTP_STATUS_CODE_BAD_REQUEST,
                  'Unable to upload data, no file uploaded nor any events.')

        if sketch:
            # Update the last activity of a sketch.
            utils.update_sketch_last_activity(sketch)

        return self._upload_events(events, form, sketch, index_name)
Exemplo n.º 16
0
    def delete(self, sketch_id, timeline_id):
        """Handles DELETE request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model
            timeline_id: Integer primary key for a timeline database model
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')
        timeline = Timeline.query.get(timeline_id)

        # Check that this timeline belongs to the sketch
        if timeline.sketch_id != sketch.id:
            if not timeline:
                msg = 'No timeline found with this ID.'
            elif not sketch:
                msg = 'No sketch found with this ID.'
            else:
                msg = (
                    'The sketch ID ({0:d}) does not match with the timeline '
                    'sketch ID ({1:d})'.format(sketch.id, timeline.sketch_id))
            abort(HTTP_STATUS_CODE_NOT_FOUND, msg)

        if not sketch.has_permission(user=current_user, permission='write'):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                'The user does not have write permission on the sketch.')

        not_delete_labels = current_app.config.get(
            'LABELS_TO_PREVENT_DELETION', [])
        for label in not_delete_labels:
            if timeline.has_label(label):
                abort(
                    HTTP_STATUS_CODE_FORBIDDEN,
                    'Timelines with label [{0:s}] cannot be deleted.'.format(
                        label))

        sketch.timelines.remove(timeline)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return HTTP_STATUS_CODE_OK
Exemplo n.º 17
0
    def post(self, sketch_id):
        """Handles POST request to the resource."""
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.")

        if not sketch.has_permission(current_user, "write"):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN, "User does not have write access on sketch."
            )

        form = request.json

        name = form.get("name")
        description = form.get("description")
        elements = form.get("elements")
        graph_config = form.get("graph_config")

        if graph_config:
            if isinstance(graph_config, dict):
                graph_json = json.dumps(graph_config)
            elif isinstance(graph_config, str):
                graph_json = graph_config
            else:
                graph_json = ""
                logger.warning("Graph config not of the correct value, not saving.")
        else:
            graph_json = ""

        graph = Graph(
            user=current_user,
            sketch=sketch,
            name=str(name),
            graph_config=graph_json,
            description=description,
            graph_elements=json.dumps(elements),
        )

        db_session.add(graph)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(graph, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 18
0
    def get(self, sketch_id, aggregation_id):  # pylint: disable=unused-argument
        """Handles GET request to the resource.

        Handler for /api/v1/sketches/:sketch_id/aggregation/:aggregation_id

        Args:
            sketch_id: Integer primary key for a sketch database model.
            aggregation_id: Integer primary key for an aggregation database
                model.

        Returns:
            JSON with aggregation results
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.")
        if not sketch.has_permission(current_user, "read"):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                "User does not have read access controls on sketch.",
            )
        aggregation = Aggregation.query.get(aggregation_id)

        # Check that this aggregation belongs to the sketch
        if aggregation.sketch_id != sketch.id:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND,
                "The sketch ID ({0:d}) does not match with the defined "
                "sketch in the aggregation ({1:d})".format(
                    aggregation.sketch_id, sketch.id
                ),
            )

        # If this is a user state view, check that it
        # belongs to the current_user
        if aggregation.name == "" and aggregation.user != current_user:
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                ("A user state view can only be viewed by the user it " "belongs to."),
            )

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(aggregation)
Exemplo n.º 19
0
    def get(self, sketch_id, group_id):
        """Handles GET request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model.
            group_id: Integer primary key for an aggregation group database
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        group = AggregationGroup.query.get(group_id)

        if not group:
            abort(HTTP_STATUS_CODE_NOT_FOUND, "No Group found with this ID.")

        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.")

        if not sketch.has_permission(user=current_user, permission="read"):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                "The user does not have read permission on the sketch.",
            )

        # Check that this group belongs to the sketch
        if group.sketch_id != sketch.id:
            msg = (
                "The sketch ID ({0:d}) does not match with the aggregation "
                "group sketch ID ({1:d})".format(sketch.id, group.sketch_id)
            )
            abort(HTTP_STATUS_CODE_FORBIDDEN, msg)

        _, objects, meta = utils.run_aggregator_group(group, sketch_id=sketch.id)

        group_fields = self.fields_registry[group.__tablename__]
        group_dict = marshal(group, group_fields)
        group_dict["agg_ids"] = [a.id for a in group.aggregations]

        objects[0].update(group_dict)

        schema = {"meta": meta, "objects": objects}

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return jsonify(schema)
Exemplo n.º 20
0
    def get(self, sketch_id):
        """Handles GET request to the resource.

        Handler for /api/v1/sketches/<int:sketch_id>/aggregation/group/

        Args:
            sketch_id: Integer primary key for a sketch database model

        Returns:
            Views in JSON (instance of flask.wrappers.Response)
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(user=current_user, permission='read'):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                'The user does not have read permission on the sketch.')
        groups = AggregationGroup.query.filter_by(
            sketch_id=sketch_id).all()
        meta = {
            'command': 'list_groups',
        }
        objects = []
        for group in groups:
            group_dict = {
                'id': group.id,
                'name': group.name,
                'parameters': group.parameters,
                'orientation': group.orientation,
                'description': group.description,
                'agg_ids': json.dumps([x.id for x in group.aggregations])
            }
            objects.append(group_dict)
        response = jsonify({'meta': meta, 'objects': objects})
        response.status_code = HTTP_STATUS_CODE_OK

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return response
Exemplo n.º 21
0
    def delete(self, sketch_id, graph_id):
        """Handles DELETE request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model
            graph_id: Integer primary key for a graph database model
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        graph = Graph.query.get(graph_id)

        if not graph:
            msg = "No Graph found with this ID."
            abort(HTTP_STATUS_CODE_NOT_FOUND, msg)

        if not sketch:
            msg = "No sketch found with this ID."
            abort(HTTP_STATUS_CODE_NOT_FOUND, msg)

        # Check that this graph belongs to the sketch
        if graph.sketch.id != sketch.id:
            msg = (
                f"The sketch ID ({sketch.id}) does not match with the story"
                f"sketch ID ({graph.sketch.id})"
            )
            abort(HTTP_STATUS_CODE_FORBIDDEN, msg)

        if not sketch.has_permission(user=current_user, permission="write"):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                "The user does not have write permission on the sketch.",
            )

        sketch.graphs.remove(graph)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return HTTP_STATUS_CODE_OK
Exemplo n.º 22
0
    def delete(self, sketch_id, aggregation_id):
        """Handles DELETE request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model.
            group_id: Integer primary key for an aggregation group database
                model.
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.")

        aggregation = Aggregation.query.get(aggregation_id)
        if not aggregation:
            abort(HTTP_STATUS_CODE_NOT_FOUND, "No aggregation found with this ID.")

        if not sketch.has_permission(user=current_user, permission="write"):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                "The user does not have write permission on the sketch.",
            )

        # Check that this aggregation belongs to the sketch
        if aggregation.sketch_id != sketch.id:
            msg = (
                "The sketch ID ({0:d}) does not match with the aggregation "
                "sketch ID ({1:d})".format(sketch.id, aggregation.sketch_id)
            )
            abort(HTTP_STATUS_CODE_FORBIDDEN, msg)

        db_session.delete(aggregation)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return HTTP_STATUS_CODE_OK
Exemplo n.º 23
0
    def post(self, sketch_id, view_id):
        """Handles POST request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model
            view_id: Integer primary key for a view database model

        Returns:
            A view in JSON (instance of flask.wrappers.Response)
        """
        form = forms.SaveViewForm.build(request)
        if not form.validate_on_submit():
            abort(HTTP_STATUS_CODE_BAD_REQUEST,
                  'Unable to update view, not able to validate form data')
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')
        if not sketch.has_permission(current_user, 'write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have write access controls on sketch.')

        view = View.query.get(view_id)
        if not view:
            abort(HTTP_STATUS_CODE_NOT_FOUND, 'No view found with this ID.')

        if view.sketch.id != sketch.id:
            abort(HTTP_STATUS_CODE_BAD_REQUEST,
                  'Unable to update view, view not attached to sketch.')

        view.query_string = form.query.data
        description = form.description.data
        if description:
            view.description = description

        query_filter = form.filter.data

        # Stripping potential pagination from views before saving it.
        if 'from' in query_filter:
            del query_filter['from']

        view.query_filter = json.dumps(query_filter, ensure_ascii=False)

        view.query_dsl = json.dumps(form.dsl.data, ensure_ascii=False)

        name = form.name.data
        if name:
            view.name = name

        view.user = current_user
        view.sketch = sketch

        if form.dsl.data:
            view.query_string = ''

        db_session.add(view)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(view, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 24
0
    def post(self):
        """Handles POST request to the resource.

        Returns:
            A view in JSON (instance of flask.wrappers.Response)
        """
        upload_enabled = current_app.config['UPLOAD_ENABLED']
        if not upload_enabled:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Failed to create timeline, upload not enabled')

        form = forms.CreateTimelineForm()
        if not form.validate_on_submit():
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Failed to create timeline, form data not validated')

        sketch_id = form.sketch_id.data
        timeline_name = form.name.data

        sketch = None
        if sketch_id:
            sketch = Sketch.query.get_with_acl(sketch_id)
            if not sketch:
                abort(
                    HTTP_STATUS_CODE_NOT_FOUND,
                    'No sketch found with this ID.')

        # We do not need a human readable filename or
        # datastore index name, so we use UUIDs here.
        index_name = uuid.uuid4().hex
        if not isinstance(index_name, six.text_type):
            index_name = codecs.decode(index_name, 'utf-8')

        # Create the search index in the Timesketch database
        searchindex = SearchIndex.get_or_create(
            name=timeline_name,
            description=timeline_name,
            user=current_user,
            index_name=index_name)
        searchindex.grant_permission(permission='read', user=current_user)
        searchindex.grant_permission(permission='write', user=current_user)
        searchindex.grant_permission(
            permission='delete', user=current_user)
        searchindex.set_status('processing')
        db_session.add(searchindex)
        db_session.commit()

        timeline = None
        if sketch and sketch.has_permission(current_user, 'write'):
            timeline = Timeline(
                name=searchindex.name,
                description=searchindex.description,
                sketch=sketch,
                user=current_user,
                searchindex=searchindex)
            sketch.timelines.append(timeline)
            db_session.add(timeline)
            db_session.commit()

        # Return Timeline if it was created.
        # pylint: disable=no-else-return
        if timeline:
            return self.to_json(
                timeline, status_code=HTTP_STATUS_CODE_CREATED)

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(
            searchindex, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 25
0
    def post(self, sketch_id, aggregation_id):
        """Handles POST request to the resource.

        Handler for /api/v1/sketches/:sketch_id/aggregation/:aggregation_id

        Args:
            sketch_id: Integer primary key for a sketch database model
            aggregation_id: Integer primary key for an aggregation database
                model
        """
        form = request.json
        if not form:
            form = request.data

        if not form:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST, 'Unable to validate form data.')

        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')
        if not sketch.has_permission(current_user, 'write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have write access controls on sketch.')

        aggregation = Aggregation.query.get(aggregation_id)
        if not aggregation:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND,
                'No aggregation found with this ID.')

        if not sketch.has_permission(user=current_user, permission='write'):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                'The user does not have write permission on the sketch.')

        aggregation.name = form.get('name', '')
        aggregation.description = form.get('description', '')
        aggregation.agg_type = form.get('agg_type', aggregation.agg_type)
        aggregation.chart_type = form.get('chart_type', aggregation.chart_type)
        aggregation.user = current_user
        aggregation.sketch = sketch

        labels = form.get('labels', '')
        if labels:
            for label in json.loads(labels):
                if aggregation.has_label(label):
                    continue
                aggregation.add_label(label)

        if form.get('parameters'):
            aggregation.parameters = json.dumps(
                form.get('parameters'), ensure_ascii=False)

        if form.get('view_id'):
            aggregation.view = form.get('view_id')

        db_session.add(aggregation)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(aggregation, status_code=HTTP_STATUS_CODE_CREATED)
Exemplo n.º 26
0
    def post(self, sketch_id, timeline_id):
        """Handles GET request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model
            timeline_id: Integer primary key for a timeline database model
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')
        timeline = Timeline.query.get(timeline_id)
        if not timeline:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND,
                'No timeline found with this ID.')

        if timeline.sketch is None:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND,
                'No sketch associated with this timeline.')

        # Check that this timeline belongs to the sketch
        if timeline.sketch.id != sketch.id:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND,
                'The sketch ID ({0:d}) does not match with the timeline '
                'sketch ID ({1:d})'.format(sketch.id, timeline.sketch.id))

        if not sketch.has_permission(user=current_user, permission='write'):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                'The user does not have write permission on the sketch.')

        form = forms.TimelineForm.build(request)
        if not form.validate_on_submit():
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST, 'Unable to validate form data.')

        if form.labels.data:
            label_string = form.labels.data
            labels = json.loads(label_string)
            if not isinstance(labels, (list, tuple)):
                abort(
                    HTTP_STATUS_CODE_BAD_REQUEST, (
                        'Label needs to be a JSON string that '
                        'converts to a list of strings.'))
            if not all([isinstance(x, str) for x in labels]):
                abort(
                    HTTP_STATUS_CODE_BAD_REQUEST, (
                        'Label needs to be a JSON string that '
                        'converts to a list of strings (not all strings)'))

            label_action = form.label_action.data
            if label_action not in ('add', 'remove'):
                abort(
                    HTTP_STATUS_CODE_BAD_REQUEST,
                    'Label action needs to be either add or remove.')

            changed = False
            if label_action == 'add':
                changes = []
                for label in labels:
                    changes.append(
                        self._add_label(timeline=timeline, label=label))
                changed = any(changes)
            elif label_action == 'remove':
                changes = []
                for label in labels:
                    changes.append(
                        self._remove_label(timeline=timeline, label=label))
                changed = any(changes)

            if not changed:
                abort(
                    HTTP_STATUS_CODE_BAD_REQUEST,
                    'Label [{0:s}] not {1:s}'.format(
                        ', '.join(labels), label_action))

            db_session.add(timeline)
            db_session.commit()
            return HTTP_STATUS_CODE_OK

        timeline.name = form.name.data
        timeline.description = form.description.data
        timeline.color = form.color.data
        db_session.add(timeline)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return HTTP_STATUS_CODE_OK
Exemplo n.º 27
0
    def delete(self, sketch_id, timeline_id):
        """Handles DELETE request to the resource.

        Args:
            sketch_id: Integer primary key for a sketch database model
            timeline_id: Integer primary key for a timeline database model
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        timeline = Timeline.query.get(timeline_id)
        if not timeline:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No timeline found with this ID.')

        if timeline.sketch is None:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND,
                'No sketch associated with this timeline.')

        # Check that this timeline belongs to the sketch
        if timeline.sketch.id != sketch.id:
            if not timeline:
                msg = 'No timeline found with this ID.'
            elif not sketch:
                msg = 'No sketch found with this ID.'
            else:
                sketch_use = sketch.id or 'No sketch ID'
                sketch_string = str(sketch_use)

                timeline_use = timeline.sketch.id or (
                    'No sketch associated with the timeline.')
                timeline_string = str(timeline_use)

                msg = (
                    'The sketch ID ({0:s}) does not match with the timeline '
                    'sketch ID ({1:s})'.format(sketch_string, timeline_string))
            abort(HTTP_STATUS_CODE_NOT_FOUND, msg)

        if not sketch.has_permission(user=current_user, permission='write'):
            abort(
                HTTP_STATUS_CODE_FORBIDDEN,
                'The user does not have write permission on the sketch.')

        not_delete_labels = current_app.config.get(
            'LABELS_TO_PREVENT_DELETION', [])
        for label in not_delete_labels:
            if timeline.has_label(label):
                abort(
                    HTTP_STATUS_CODE_FORBIDDEN,
                    'Timelines with label [{0:s}] cannot be deleted.'.format(
                        label))

        # Check if this searchindex is used in other sketches.
        close_index = True
        searchindex = timeline.searchindex
        index_name = searchindex.index_name
        search_indices = SearchIndex.query.filter_by(
            index_name=index_name).all()
        timelines = []
        for index in search_indices:
            timelines.extend(index.timelines)

        for timeline_ in timelines:
            if timeline_.sketch is None:
                continue

            if timeline_.sketch.id != sketch.id:
                close_index = False
                break

            if timeline_.id != timeline_id:
                # There are more than a single timeline using this index_name,
                # we can't close it (unless this timeline is archived).
                if timeline_.get_status.status != 'archived':
                    close_index = False
                    break

        if close_index:
            try:
                self.datastore.client.indices.close(
                    index=searchindex.index_name)
            except elasticsearch.NotFoundError:
                logger.error(
                    'Unable to close index: {0:s} - index not '
                    'found'.format(searchindex.index_name))

            searchindex.set_status(status='archived')
            timeline.set_status(status='archived')

        sketch.timelines.remove(timeline)
        db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return HTTP_STATUS_CODE_OK
Exemplo n.º 28
0
    def post(self, sketch_id):
        """Handles POST request to the resource.

        Returns:
            Graph in JSON (instance of flask.wrappers.Response)
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.")

        form = request.json
        plugin_name = form.get("plugin")
        graph_config = form.get("config")
        refresh = form.get("refresh")
        timeline_ids = form.get("timeline_ids", None)

        if timeline_ids and not isinstance(timeline_ids, (list, tuple)):
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                "Timeline IDs if supplied need to be a list.",
            )

        if timeline_ids and not all([isinstance(x, int) for x in timeline_ids]):
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                "Timeline IDs needs to be a list of integers.",
            )

        sketch_indices = [
            timeline.searchindex.index_name for timeline in sketch.active_timelines
        ]

        cache = GraphCache.get_or_create(sketch=sketch, graph_plugin=plugin_name)

        if graph_config:
            cache_config = graph_config
        else:
            cache_config = cache.graph_config

        if isinstance(cache_config, str):
            cache_config = json.loads(cache_config)

        # Refresh cache if timelines have been added/removed from the sketch.
        if cache_config:
            cache_graph_filter = cache_config.get("filter", {})
            cache_filter_indices = cache_graph_filter.get("indices", [])
            if set(sketch_indices) ^ set(cache_filter_indices):
                refresh = True

        if cache.graph_elements and not refresh:
            return self.to_json(cache)

        graph_class = manager.GraphManager.get_graph(plugin_name)
        graph = graph_class(sketch=sketch, timeline_ids=timeline_ids)
        cytoscape_json = graph.generate().to_cytoscape()

        if cytoscape_json:
            cache.graph_elements = json.dumps(cytoscape_json)
            cache.graph_config = json.dumps(graph_config)
            cache.update_modification_time()
            db_session.add(cache)
            db_session.commit()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(cache)
Exemplo n.º 29
0
    def post(self, sketch_id):
        """Handles POST request to the resource.

        Returns:
            A sketch in JSON (instance of flask.wrappers.Response)
        """
        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(current_user, 'write'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have write access controls on sketch.')

        form = request.json
        if not form:
            form = request.data

        metadata = {'created': True}

        searchindex_id = form.get('timeline', 0)
        if isinstance(searchindex_id, str) and searchindex_id.isdigit():
            searchindex_id = int(searchindex_id)

        if not isinstance(searchindex_id, int):
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'The timeline (searchindex id) needs to be an integer.')

        searchindex = SearchIndex.query.get_with_acl(searchindex_id)
        if searchindex.get_status.status == 'deleted':
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Unable to create a timeline using a deleted search index')

        timeline_id = [
            t.searchindex.id for t in sketch.timelines
            if t.searchindex.id == searchindex_id
        ]

        if not timeline_id:
            return_code = HTTP_STATUS_CODE_CREATED
            timeline_name = form.get('timeline_name', searchindex.name)
            timeline = Timeline(
                name=timeline_name,
                description=searchindex.description,
                sketch=sketch,
                user=current_user,
                searchindex=searchindex)
            sketch.timelines.append(timeline)
            labels_to_prevent_deletion = current_app.config.get(
                'LABELS_TO_PREVENT_DELETION', [])

            for label in sketch.get_labels:
                if label not in labels_to_prevent_deletion:
                    continue
                timeline.add_label(label)
                searchindex.add_label(label)

            # Set status to ready so the timeline can be queried.
            timeline.set_status('ready')

            db_session.add(timeline)
            db_session.commit()
        else:
            metadata['created'] = False
            return_code = HTTP_STATUS_CODE_OK
            timeline = Timeline.query.get(timeline_id)

        # Run sketch analyzers when timeline is added. Import here to avoid
        # circular imports.
        # pylint: disable=import-outside-toplevel
        if current_app.config.get('AUTO_SKETCH_ANALYZERS'):
            # pylint: disable=import-outside-toplevel
            from timesketch.lib import tasks
            sketch_analyzer_group, _ = tasks.build_sketch_analysis_pipeline(
                sketch_id, searchindex_id, current_user.id,
                timeline_id=timeline_id)
            if sketch_analyzer_group:
                pipeline = (tasks.run_sketch_init.s(
                    [searchindex.index_name]) | sketch_analyzer_group)
                pipeline.apply_async()

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return self.to_json(
            timeline, meta=metadata, status_code=return_code)
Exemplo n.º 30
0
    def post(self, sketch_id):
        """Handles POST request to the resource.

        Handler for /api/v1/sketches/<int:sketch_id>/aggregation/explore/

        Args:
            sketch_id: Integer primary key for a sketch database model

        Returns:
            JSON with aggregation results
        """
        form = forms.AggregationExploreForm.build(request)
        if not form.validate_on_submit():
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Not able to run aggregation, unable to validate form data.')

        sketch = Sketch.query.get_with_acl(sketch_id)
        if not sketch:
            abort(
                HTTP_STATUS_CODE_NOT_FOUND, 'No sketch found with this ID.')

        if not sketch.has_permission(current_user, 'read'):
            abort(HTTP_STATUS_CODE_FORBIDDEN,
                  'User does not have read access controls on sketch.')

        if sketch.get_status.status == 'archived':
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'Not able to run aggregation on an archived sketch.')

        sketch_indices = {
            t.searchindex.index_name
            for t in sketch.timelines
            if t.get_status.status.lower() == 'ready'
        }

        aggregation_dsl = form.aggregation_dsl.data
        aggregator_name = form.aggregator_name.data

        if aggregator_name:
            if isinstance(form.aggregator_parameters.data, dict):
                aggregator_parameters = form.aggregator_parameters.data
            else:
                aggregator_parameters = json.loads(
                    form.aggregator_parameters.data)

            agg_class = aggregator_manager.AggregatorManager.get_aggregator(
                aggregator_name)
            if not agg_class:
                return {}
            if not aggregator_parameters:
                aggregator_parameters = {}

            indices = aggregator_parameters.pop('index', sketch_indices)
            indices, timeline_ids = lib_utils.get_validated_indices(
                indices, sketch)

            aggregator = agg_class(
                sketch_id=sketch_id, indices=indices,
                timeline_ids=timeline_ids)

            chart_type = aggregator_parameters.pop('supported_charts', None)
            chart_color = aggregator_parameters.pop('chart_color', '')
            chart_title = aggregator_parameters.pop(
                'chart_title', aggregator.chart_title)

            time_before = time.time()
            try:
                result_obj = aggregator.run(**aggregator_parameters)
            except NotFoundError:
                abort(
                    HTTP_STATUS_CODE_NOT_FOUND,
                    'Attempting to run an aggregation on a non-existing '
                    'Elastic index, index: {0:s} and parameters: {1!s}'.format(
                        ','.join(indices), aggregator_parameters))
            except ValueError as exc:
                abort(
                    HTTP_STATUS_CODE_BAD_REQUEST,
                    'Unable to run the aggregation, with error: {0!s}'.format(
                        exc))
            time_after = time.time()

            aggregator_description = aggregator.describe

            buckets = result_obj.to_dict()
            buckets['buckets'] = buckets.pop('values')
            result = {
                'aggregation_result': {
                    aggregator_name: buckets
                }
            }
            meta = {
                'method': 'aggregator_run',
                'chart_type': chart_type,
                'name': aggregator_description.get('name'),
                'description': aggregator_description.get('description'),
                'es_time': time_after - time_before,
            }

            if chart_type:
                meta['vega_spec'] = result_obj.to_chart(
                    chart_name=chart_type,
                    chart_title=chart_title, color=chart_color)
                meta['vega_chart_title'] = chart_title

        elif aggregation_dsl:
            # pylint: disable=unexpected-keyword-arg
            result = self.datastore.client.search(
                index=','.join(sketch_indices), body=aggregation_dsl, size=0)

            meta = {
                'es_time': result.get('took', 0),
                'es_total_count': result.get('hits', {}).get('total', 0),
                'timed_out': result.get('timed_out', False),
                'method': 'aggregator_query',
                'max_score': result.get('hits', {}).get('max_score', 0.0)
            }
        else:
            abort(
                HTTP_STATUS_CODE_BAD_REQUEST,
                'An aggregation DSL or a name for an aggregator name needs '
                'to be provided!')

        result_keys = set(result.keys()) - self.REMOVE_FIELDS
        objects = [result[key] for key in result_keys]
        schema = {'meta': meta, 'objects': objects}

        # Update the last activity of a sketch.
        utils.update_sketch_last_activity(sketch)

        return jsonify(schema)