diff --git a/contrib/gcs_importer.py b/contrib/gcs_importer.py index 76c716b038..9198daab31 100644 --- a/contrib/gcs_importer.py +++ b/contrib/gcs_importer.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. """Google Cloud Storage importer.""" +# Unmaintained contrib. Skip linting this file. +# pylint: skip-file import argparse import time @@ -80,12 +82,12 @@ def setup_sketch(timeline_name, index_name, username, sketch_id=None): (tuple) sketch ID and timeline ID as integers """ with app.app_context(): - user = User.get_or_create(username=username) + user = User.get_or_create(username=username, name=username) sketch = None if sketch_id: try: - sketch = Sketch.query.get_with_acl(sketch_id, user=user) + sketch = Sketch.get_with_acl(sketch_id, user=user) logger.info( "Using existing sketch: {} ({})".format(sketch.name, sketch.id) ) diff --git a/timesketch/api/v1/resources/aggregation.py b/timesketch/api/v1/resources/aggregation.py index 3a10a901f4..751d115dfc 100644 --- a/timesketch/api/v1/resources/aggregation.py +++ b/timesketch/api/v1/resources/aggregation.py @@ -59,7 +59,7 @@ def get(self, sketch_id, aggregation_id): # pylint: disable=unused-argument Returns: JSON with aggregation results """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -67,7 +67,7 @@ def get(self, sketch_id, aggregation_id): # pylint: disable=unused-argument HTTP_STATUS_CODE_FORBIDDEN, "User does not have read access controls on sketch.", ) - aggregation = Aggregation.query.get(aggregation_id) + aggregation = Aggregation.get_by_id(aggregation_id) # Check that this aggregation belongs to the sketch if aggregation.sketch_id != sketch.id: @@ -111,7 +111,7 @@ def post(self, sketch_id, aggregation_id): if not form: abort(HTTP_STATUS_CODE_BAD_REQUEST, "Unable to validate form data.") - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -120,7 +120,7 @@ def post(self, sketch_id, aggregation_id): "User does not have write access controls on sketch.", ) - aggregation = Aggregation.query.get(aggregation_id) + aggregation = Aggregation.get_by_id(aggregation_id) if not aggregation: abort(HTTP_STATUS_CODE_NOT_FOUND, "No aggregation found with this ID.") @@ -169,11 +169,11 @@ def delete(self, sketch_id, aggregation_id): group_id: Integer primary key for an aggregation group database model. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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) + aggregation = Aggregation.get_by_id(aggregation_id) if not aggregation: abort(HTTP_STATUS_CODE_NOT_FOUND, "No aggregation found with this ID.") @@ -274,8 +274,8 @@ def get(self, sketch_id, group_id): 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) + sketch = Sketch.get_with_acl(sketch_id) + group = AggregationGroup.get_by_id(group_id) if not group: abort(HTTP_STATUS_CODE_NOT_FOUND, "No Group found with this ID.") @@ -321,8 +321,8 @@ def post(self, sketch_id, group_id): 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) + sketch = Sketch.get_with_acl(sketch_id) + group = AggregationGroup.get_by_id(group_id) if not group: abort(HTTP_STATUS_CODE_NOT_FOUND, "No Group found with this ID.") @@ -362,7 +362,7 @@ def post(self, sketch_id, group_id): aggregations = [] for agg_id in agg_ids: - aggregation = Aggregation.query.get(agg_id) + aggregation = Aggregation.get_by_id(agg_id) if not aggregation: abort( HTTP_STATUS_CODE_BAD_REQUEST, @@ -386,8 +386,8 @@ def delete(self, sketch_id, group_id): 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) + sketch = Sketch.get_with_acl(sketch_id) + group = AggregationGroup.get_by_id(group_id) if not group: abort(HTTP_STATUS_CODE_NOT_FOUND, "No Group found with this ID.") @@ -442,7 +442,7 @@ def post(self, sketch_id): "Not able to run aggregation, unable to validate form data.", ) - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -579,7 +579,7 @@ def get(self, sketch_id): Returns: Views in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -656,7 +656,7 @@ def post(self, sketch_id): if not form: abort(HTTP_STATUS_CODE_BAD_REQUEST, "Unable to validate form data.") - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -689,7 +689,7 @@ def get(self, sketch_id): Returns: Views in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -731,7 +731,7 @@ def post(self, sketch_id): Returns: An aggregation in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") diff --git a/timesketch/api/v1/resources/analysis.py b/timesketch/api/v1/resources/analysis.py index c0b0d9b0e6..e141f80421 100644 --- a/timesketch/api/v1/resources/analysis.py +++ b/timesketch/api/v1/resources/analysis.py @@ -61,7 +61,7 @@ def get(self, sketch_id, timeline_id): Returns: An analysis in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -70,7 +70,7 @@ def get(self, sketch_id, timeline_id): HTTP_STATUS_CODE_FORBIDDEN, "User does not have read access to sketch" ) - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) if not timeline: abort(HTTP_STATUS_CODE_NOT_FOUND, "No timeline found with this ID.") @@ -96,7 +96,7 @@ def get(self, sketch_id): Returns: A analyzer session in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -152,7 +152,7 @@ def get(self, sketch_id, session_id): Returns: A analyzer session in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -162,7 +162,7 @@ def get(self, sketch_id, session_id): HTTP_STATUS_CODE_FORBIDDEN, "User does not have read access to sketch" ) - analysis_session = AnalysisSession.query.get(session_id) + analysis_session = AnalysisSession.get_by_id(session_id) return self.to_json(analysis_session) @@ -181,7 +181,7 @@ def get(self, sketch_id): * description: Description of the analyzer provided in the class * is_multi: Boolean indicating if the analyzer is a multi analyzer """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -216,7 +216,7 @@ def post(self, sketch_id): Returns: A string with the response from running the analyzer. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -284,7 +284,7 @@ def post(self, sketch_id): # TODO: Change to run on Timeline instead of Index sessions = [] for timeline_id in timeline_ids: - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) if not timeline: continue if not timeline.status[0].status == "ready": diff --git a/timesketch/api/v1/resources/archive.py b/timesketch/api/v1/resources/archive.py index 8753e7fe37..dbbd449c24 100644 --- a/timesketch/api/v1/resources/archive.py +++ b/timesketch/api/v1/resources/archive.py @@ -82,9 +82,9 @@ def get(self, sketch_id): A sketch in JSON (instance of flask.wrappers.Response) """ if current_user.admin: - sketch = Sketch.query.get(sketch_id) + sketch = Sketch.get_by_id(sketch_id) else: - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -121,9 +121,9 @@ def post(self, sketch_id): A sketch in JSON (instance of flask.wrappers.Response) """ if current_user.admin: - sketch = Sketch.query.get(sketch_id) + sketch = Sketch.get_by_id(sketch_id) else: - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") diff --git a/timesketch/api/v1/resources/attribute.py b/timesketch/api/v1/resources/attribute.py index 63e9a50826..cc09e56484 100644 --- a/timesketch/api/v1/resources/attribute.py +++ b/timesketch/api/v1/resources/attribute.py @@ -69,7 +69,7 @@ def get(self, sketch_id): Returns: An analysis in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -87,7 +87,7 @@ def post(self, sketch_id): Returns: A HTTP 200 if the attribute is successfully added or modified. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -193,7 +193,7 @@ def delete(self, sketch_id): Returns: A HTTP response code. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -231,6 +231,7 @@ def delete(self, sketch_id): for value in attribute.values: attribute.values.remove(value) sketch.attributes.remove(attribute) + db_session.add(sketch) db_session.commit() return HTTP_STATUS_CODE_OK diff --git a/timesketch/api/v1/resources/datafinder.py b/timesketch/api/v1/resources/datafinder.py index 9c0c3a957f..b277a6d692 100644 --- a/timesketch/api/v1/resources/datafinder.py +++ b/timesketch/api/v1/resources/datafinder.py @@ -44,7 +44,7 @@ def post(self, sketch_id): Returns: A list of JSON representations of the data sources. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") diff --git a/timesketch/api/v1/resources/datasource.py b/timesketch/api/v1/resources/datasource.py index 18741e04d6..8b3da0b333 100644 --- a/timesketch/api/v1/resources/datasource.py +++ b/timesketch/api/v1/resources/datasource.py @@ -49,7 +49,7 @@ def get(self, sketch_id): Returns: A list of JSON representations of the data sources. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -85,7 +85,7 @@ def post(self, sketch_id): Returns: A datasource in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -112,7 +112,7 @@ def post(self, sketch_id): "Unable to create a data source without a timeline " "identifier.", ) - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) if not timeline: abort(HTTP_STATUS_CODE_NOT_FOUND, "No timeline found with this ID.") @@ -150,7 +150,7 @@ def _verify_sketch_and_datasource(self, sketch_id, datasource_id): This function aborts if the ACLs on the sketch are not sufficient and the data source does not belong to the sketch in question. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -160,7 +160,7 @@ def _verify_sketch_and_datasource(self, sketch_id, datasource_id): "Unable to fetch data sources from an archived sketch.", ) - data_source = DataSource.query.get(datasource_id) + data_source = DataSource.get_by_id(datasource_id) if not data_source: abort(HTTP_STATUS_CODE_NOT_FOUND, "No DataSource found with this ID.") @@ -182,7 +182,7 @@ def get(self, sketch_id, datasource_id): A JSON representation of the data source. """ self._verify_sketch_and_datasource(sketch_id, datasource_id) - data_source = DataSource.query.get(datasource_id) + data_source = DataSource.get_by_id(datasource_id) return self.to_json(data_source) @login_required @@ -198,7 +198,7 @@ def post(self, sketch_id, datasource_id): """ self._verify_sketch_and_datasource(sketch_id, datasource_id) - data_source = DataSource.query.get(datasource_id) + data_source = DataSource.get_by_id(datasource_id) changed = False form = request.json diff --git a/timesketch/api/v1/resources/event.py b/timesketch/api/v1/resources/event.py index 0e0c280165..7952893a9b 100644 --- a/timesketch/api/v1/resources/event.py +++ b/timesketch/api/v1/resources/event.py @@ -108,7 +108,7 @@ def post(self, sketch_id): Returns: An annotation in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -266,7 +266,7 @@ def get(self, sketch_id): """ args = self.parser.parse_args() - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -422,7 +422,7 @@ def post(self, sketch_id): includes metrics on events modified, attributes added, chunks per index, number of errors and the last 10 errors. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -549,7 +549,7 @@ def post(self, sketch_id): Returns: An annotation in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: msg = "No sketch found with this ID." abort(HTTP_STATUS_CODE_NOT_FOUND, msg) @@ -770,7 +770,7 @@ def _get_sketch(self, sketch_id): Returns: Sketch object """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -791,7 +791,7 @@ def _get_current_search_node(self, current_search_node_id, sketch): Returns: Search history object representing the current search node """ - current_search_node = SearchHistory.query.get(current_search_node_id) + current_search_node = SearchHistory.get_by_id(current_search_node_id) if not current_search_node: abort( HTTP_STATUS_CODE_NOT_FOUND, @@ -1121,7 +1121,7 @@ def get(self, sketch_id): Returns: Number of events in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -1154,7 +1154,7 @@ def post(self, sketch_id): Returns: An annotation in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -1183,7 +1183,7 @@ def post(self, sketch_id): index_name=searchindex_name ).first() elif searchindex_id: - searchindex = SearchIndex.query.get(searchindex_id) + searchindex = SearchIndex.get_by_id(searchindex_id) if not searchindex: abort( @@ -1201,7 +1201,7 @@ def post(self, sketch_id): if not timeline_id: abort(HTTP_STATUS_CODE_NOT_FOUND, "No timeline identifier supplied.") - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) if not timeline: abort(HTTP_STATUS_CODE_NOT_FOUND, "No Timeline found with this ID.") @@ -1303,7 +1303,7 @@ def post(self, sketch_id): to the sketch """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -1359,7 +1359,7 @@ def post(self, sketch_id): index_name=searchindex_name ).first() elif searchindex_id: - searchindex = SearchIndex.query.get(searchindex_id) + searchindex = SearchIndex.get_by_id(searchindex_id) if not searchindex: abort( diff --git a/timesketch/api/v1/resources/explore.py b/timesketch/api/v1/resources/explore.py index 166cb557ce..6572d71414 100644 --- a/timesketch/api/v1/resources/explore.py +++ b/timesketch/api/v1/resources/explore.py @@ -73,7 +73,7 @@ def post(self, sketch_id): Returns: JSON with list of matched events """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -106,7 +106,7 @@ def post(self, sketch_id): question_id = request.json.get("question", None) if scenario_id: - scenario = Scenario.query.get(scenario_id) + scenario = Scenario.get_by_id(scenario_id) if scenario: if scenario.sketch_id != sketch.id: abort( @@ -115,7 +115,7 @@ def post(self, sketch_id): ) if facet_id: - facet = Facet.query.get(facet_id) + facet = Facet.get_by_id(facet_id) if facet: if facet.scenario.sketch_id != sketch.id: abort( @@ -124,7 +124,7 @@ def post(self, sketch_id): ) if question_id: - question = InvestigativeQuestion.query.get(question_id) + question = InvestigativeQuestion.get_by_id(question_id) if question: if question.facet.scenario.sketch_id != sketch.id: abort( @@ -361,7 +361,7 @@ def post(self, sketch_id): new_search = SearchHistory(user=current_user, sketch=sketch) if parent: - previous_search = SearchHistory.query.get(parent) + previous_search = SearchHistory.get_by_id(parent) else: previous_search = ( SearchHistory.query.filter_by(user=current_user, sketch=sketch) @@ -461,7 +461,7 @@ def post(self, sketch_id): form = forms.ExploreForm.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) + sketch = Sketch.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"): @@ -505,7 +505,7 @@ def get(self, sketch_id): if not limit: limit = DEFAULT_LIMIT - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -544,7 +544,7 @@ def get(self, sketch_id): Returns: Search history in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") diff --git a/timesketch/api/v1/resources/graph.py b/timesketch/api/v1/resources/graph.py index 417c3a1d95..33fb32106f 100644 --- a/timesketch/api/v1/resources/graph.py +++ b/timesketch/api/v1/resources/graph.py @@ -51,7 +51,7 @@ def get(self, sketch_id): Returns: List of graphs in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -61,7 +61,7 @@ def get(self, sketch_id): @login_required def post(self, sketch_id): """Handles POST request to the resource.""" - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -124,11 +124,11 @@ def get(self, sketch_id, graph_id): args = self.parser.parse_args() output_format = args.get("format", None) - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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) + graph = Graph.get_by_id(graph_id) if not graph: abort(HTTP_STATUS_CODE_NOT_FOUND, "No graph found with this ID.") @@ -171,7 +171,7 @@ def post(self, sketch_id, graph_id): Returns: List of graphs in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -181,7 +181,7 @@ def post(self, sketch_id, graph_id): "User does not have write access controls on sketch.", ) - graph = Graph.query.get(graph_id) + graph = Graph.get_by_id(graph_id) if not graph: abort(HTTP_STATUS_CODE_NOT_FOUND, "No graph found with this ID.") @@ -230,8 +230,8 @@ def delete(self, sketch_id, graph_id): 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) + sketch = Sketch.get_with_acl(sketch_id) + graph = Graph.get_by_id(graph_id) if not graph: msg = "No Graph found with this ID." @@ -297,7 +297,7 @@ def post(self, sketch_id): Returns: Graph in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") diff --git a/timesketch/api/v1/resources/scenarios.py b/timesketch/api/v1/resources/scenarios.py index 16854e78ac..f42cad000b 100644 --- a/timesketch/api/v1/resources/scenarios.py +++ b/timesketch/api/v1/resources/scenarios.py @@ -90,7 +90,7 @@ def get(self, sketch_id): """ args = self.parser.parse_args() filter_on_status = args.get("status") - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -115,7 +115,7 @@ def post(self, sketch_id): Returns: A JSON representation of the scenario. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -232,8 +232,8 @@ def get(self, sketch_id, scenario_id): Returns: A list of JSON representations of the scenarios. """ - sketch = Sketch.query.get_with_acl(sketch_id) - scenario = Scenario.query.get(scenario_id) + sketch = Sketch.get_with_acl(sketch_id) + scenario = Scenario.get_by_id(scenario_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID") @@ -258,8 +258,8 @@ def post(self, sketch_id, scenario_id): Returns: A JSON representation of the scenario. """ - sketch = Sketch.query.get_with_acl(sketch_id) - scenario = Scenario.query.get(scenario_id) + sketch = Sketch.get_with_acl(sketch_id) + scenario = Scenario.get_by_id(scenario_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID") @@ -295,8 +295,8 @@ def post(self, sketch_id, scenario_id): Returns: A JSON representation of the updated scenario. """ - sketch = Sketch.query.get_with_acl(sketch_id) - scenario = Scenario.query.get(scenario_id) + sketch = Sketch.get_with_acl(sketch_id) + scenario = Scenario.get_by_id(scenario_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID") @@ -333,11 +333,11 @@ def get(self, sketch_id, question_id): Returns: A list of JSON representations of the conclusions. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID") - question = InvestigativeQuestion.query.get(question_id) + question = InvestigativeQuestion.get_by_id(question_id) if not question: abort(HTTP_STATUS_CODE_NOT_FOUND, "No question found with this ID") @@ -356,11 +356,11 @@ def post(self, sketch_id, question_id): Returns: A JSON representation of the conclusion. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID") - question = InvestigativeQuestion.query.get(question_id) + question = InvestigativeQuestion.get_by_id(question_id) if not question: abort(HTTP_STATUS_CODE_NOT_FOUND, "No question found with this ID") @@ -393,15 +393,15 @@ def put(self, sketch_id, question_id, conclusion_id): Returns: A JSON representation of the conclusion. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID") - question = InvestigativeQuestion.query.get(question_id) + question = InvestigativeQuestion.get_by_id(question_id) if not question: abort(HTTP_STATUS_CODE_NOT_FOUND, "No question found with this ID") - conclusion = InvestigativeQuestionConclusion.query.get(conclusion_id) + conclusion = InvestigativeQuestionConclusion.get_by_id(conclusion_id) if not conclusion: abort(HTTP_STATUS_CODE_NOT_FOUND, "No conclusion found with this ID") @@ -426,15 +426,15 @@ def delete(self, sketch_id, question_id, conclusion_id): Deletes a conclusion. """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID") - question = InvestigativeQuestion.query.get(question_id) + question = InvestigativeQuestion.get_by_id(question_id) if not question: abort(HTTP_STATUS_CODE_NOT_FOUND, "No question found with this ID") - conclusion = InvestigativeQuestionConclusion.query.get(conclusion_id) + conclusion = InvestigativeQuestionConclusion.get_by_id(conclusion_id) if not conclusion: abort(HTTP_STATUS_CODE_NOT_FOUND, "No conclusion found with this ID") diff --git a/timesketch/api/v1/resources/searchindex.py b/timesketch/api/v1/resources/searchindex.py index 853f8add44..12a60facc7 100644 --- a/timesketch/api/v1/resources/searchindex.py +++ b/timesketch/api/v1/resources/searchindex.py @@ -107,7 +107,7 @@ def get(self, searchindex_id): Returns: Search index in JSON (instance of flask.wrappers.Response) """ - searchindex = SearchIndex.query.get_with_acl(searchindex_id) + searchindex = SearchIndex.get_with_acl(searchindex_id) try: mapping = self.datastore.client.indices.get_mapping(searchindex.index_name) @@ -144,7 +144,7 @@ def post(self, searchindex_id): if not searchindex_id: abort(HTTP_STATUS_CODE_BAD_REQUEST, "Need to define a search index ID") - searchindex = SearchIndex.query.get_with_acl(searchindex_id) + searchindex = SearchIndex.get_with_acl(searchindex_id) if not searchindex: abort(HTTP_STATUS_CODE_NOT_FOUND, "No searchindex found with this ID.") @@ -184,7 +184,7 @@ def post(self, searchindex_id): @login_required def delete(self, searchindex_id): """Handles DELETE request to the resource.""" - searchindex = SearchIndex.query.get_with_acl(searchindex_id) + searchindex = SearchIndex.get_with_acl(searchindex_id) if not searchindex: abort(HTTP_STATUS_CODE_NOT_FOUND, "No searchindex found with this ID.") diff --git a/timesketch/api/v1/resources/searchtemplate.py b/timesketch/api/v1/resources/searchtemplate.py index 1f3b0dc75d..ea7b7140a6 100644 --- a/timesketch/api/v1/resources/searchtemplate.py +++ b/timesketch/api/v1/resources/searchtemplate.py @@ -49,7 +49,7 @@ def get(self, searchtemplate_id): Returns: Search template in JSON (instance of flask.wrappers.Response) """ - searchtemplate = SearchTemplate.query.get(searchtemplate_id) + searchtemplate = SearchTemplate.get_by_id(searchtemplate_id) if not searchtemplate: abort(HTTP_STATUS_CODE_NOT_FOUND, "Search template was not found") @@ -66,7 +66,7 @@ def delete(self, searchtemplate_id): Returns: HTTP status 200 if successful, otherwise error messages. """ - searchtemplate = SearchTemplate.query.get(searchtemplate_id) + searchtemplate = SearchTemplate.get_by_id(searchtemplate_id) if not searchtemplate: abort(HTTP_STATUS_CODE_NOT_FOUND, "Search template was not found") @@ -101,7 +101,7 @@ def post(self, searchtemplate_id): Parsed and sanitized search query string. """ form = request.json or {} - searchtemplate = SearchTemplate.query.get(searchtemplate_id) + searchtemplate = SearchTemplate.get_by_id(searchtemplate_id) if not searchtemplate: abort(HTTP_STATUS_CODE_NOT_FOUND, "Search template was not found") @@ -167,7 +167,7 @@ def post(self): "Unable to save the searchtemplate, the saved search ID is " "missing.", ) - search_obj = View.query.get(search_id) + search_obj = View.get_by_id(search_id) if not search_obj: abort(HTTP_STATUS_CODE_NOT_FOUND, "No search found with this ID.") @@ -181,7 +181,7 @@ def post(self): "This search has already been saved as a template.", ) - sketch = Sketch.query.get_with_acl(search_obj.sketch.id) + sketch = Sketch.get_with_acl(search_obj.sketch.id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") diff --git a/timesketch/api/v1/resources/session.py b/timesketch/api/v1/resources/session.py index 29d62b7a7c..b39f4035ce 100644 --- a/timesketch/api/v1/resources/session.py +++ b/timesketch/api/v1/resources/session.py @@ -47,7 +47,7 @@ def get(self, sketch_id, timeline_index): sessions = [] is_truncated = False - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): diff --git a/timesketch/api/v1/resources/sketch.py b/timesketch/api/v1/resources/sketch.py index 52adab3ac1..07dd68b81a 100644 --- a/timesketch/api/v1/resources/sketch.py +++ b/timesketch/api/v1/resources/sketch.py @@ -207,11 +207,11 @@ def post(self): form = forms.NameDescriptionForm.build(request) if not form.validate_on_submit(): abort(HTTP_STATUS_CODE_BAD_REQUEST, "Unable to validate form data.") - sketch = Sketch( - name=form.name.data, description=form.description.data, user=current_user - ) - sketch.status.append(sketch.Status(user=None, status="new")) + + sketch = Sketch(name=form.name.data, description=form.description.data) db_session.add(sketch) + sketch.user = current_user + sketch.status.append(sketch.Status(user=None, status="new")) db_session.commit() # Give the requesting user permissions on the new sketch. @@ -301,11 +301,11 @@ def get(self, sketch_id): A sketch in JSON (instance of flask.wrappers.Response) """ if current_user.admin: - sketch = Sketch.query.get(sketch_id) + sketch = Sketch.get_by_id(sketch_id) if not sketch.has_permission(current_user, "read"): return self._get_sketch_for_admin(sketch) else: - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -473,7 +473,7 @@ def get(self, sketch_id): @login_required def delete(self, sketch_id): """Handles DELETE request to the resource.""" - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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, "delete"): @@ -498,7 +498,7 @@ def post(self, sketch_id): Returns: A sketch in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") diff --git a/timesketch/api/v1/resources/story.py b/timesketch/api/v1/resources/story.py index 23389b5622..2936235387 100644 --- a/timesketch/api/v1/resources/story.py +++ b/timesketch/api/v1/resources/story.py @@ -49,7 +49,7 @@ def get(self, sketch_id): Returns: Stories in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -79,7 +79,7 @@ def post(self, sketch_id): 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) + sketch = Sketch.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"): @@ -148,8 +148,8 @@ def get(self, sketch_id, story_id): Returns: A story in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) - story = Story.query.get(story_id) + sketch = Sketch.get_with_acl(sketch_id) + story = Story.get_by_id(story_id) if not story: msg = "No Story found with this ID." @@ -193,8 +193,8 @@ def post(self, sketch_id, story_id): Returns: A view in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) - story = Story.query.get(story_id) + sketch = Sketch.get_with_acl(sketch_id) + story = Story.get_by_id(story_id) if not story: msg = "No Story found with this ID." @@ -247,8 +247,8 @@ def delete(self, sketch_id, story_id): 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) + sketch = Sketch.get_with_acl(sketch_id) + story = Story.get_by_id(story_id) if not story: msg = "No Story found with this ID." diff --git a/timesketch/api/v1/resources/timeline.py b/timesketch/api/v1/resources/timeline.py index 8ef9584bca..afdc964d5a 100644 --- a/timesketch/api/v1/resources/timeline.py +++ b/timesketch/api/v1/resources/timeline.py @@ -54,7 +54,7 @@ def get(self, sketch_id): Returns: View in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -71,7 +71,7 @@ def post(self, sketch_id): Returns: A sketch in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -97,7 +97,7 @@ def post(self, sketch_id): "The timeline (searchindex id) needs to be an integer.", ) - searchindex = SearchIndex.query.get_with_acl(searchindex_id) + searchindex = SearchIndex.get_with_acl(searchindex_id) if searchindex.get_status.status == "deleted": abort( HTTP_STATUS_CODE_BAD_REQUEST, @@ -139,7 +139,7 @@ def post(self, sketch_id): else: metadata["created"] = False return_code = HTTP_STATUS_CODE_OK - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) # Run sketch analyzers when timeline is added. Import here to avoid # circular imports. @@ -197,11 +197,11 @@ def get(self, sketch_id, timeline_id): 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) + sketch = Sketch.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) + timeline = Timeline.get_by_id(timeline_id) if not timeline: abort(HTTP_STATUS_CODE_NOT_FOUND, "No Timeline found with this ID.") @@ -255,10 +255,10 @@ def post(self, sketch_id, timeline_id): 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) + sketch = Sketch.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) + timeline = Timeline.get_by_id(timeline_id) if not timeline: abort(HTTP_STATUS_CODE_NOT_FOUND, "No timeline found with this ID.") @@ -353,11 +353,11 @@ def delete(self, sketch_id, timeline_id): 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) + sketch = Sketch.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) + timeline = Timeline.get_by_id(timeline_id) if not timeline: abort(HTTP_STATUS_CODE_NOT_FOUND, "No timeline found with this ID.") @@ -475,7 +475,7 @@ def post(self): sketch = None if sketch_id: - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") diff --git a/timesketch/api/v1/resources/upload.py b/timesketch/api/v1/resources/upload.py index 25ba23e511..b9a06fc4d8 100644 --- a/timesketch/api/v1/resources/upload.py +++ b/timesketch/api/v1/resources/upload.py @@ -497,7 +497,7 @@ def post(self): if not isinstance(sketch_id, int): sketch_id = int(sketch_id) - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") diff --git a/timesketch/api/v1/resources/user.py b/timesketch/api/v1/resources/user.py index 17840b3a32..e825ac12a1 100644 --- a/timesketch/api/v1/resources/user.py +++ b/timesketch/api/v1/resources/user.py @@ -110,7 +110,7 @@ def post(self, sketch_id): Args: sketch_id: Integer primary key for a sketch database model """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") form = request.json diff --git a/timesketch/api/v1/resources/view.py b/timesketch/api/v1/resources/view.py index 6e3698076d..09dd9d450f 100644 --- a/timesketch/api/v1/resources/view.py +++ b/timesketch/api/v1/resources/view.py @@ -72,7 +72,7 @@ def create_view_from_form(sketch, form): if form.from_searchtemplate_id.data: # Get the template from the datastore template_id = form.from_searchtemplate_id.data - searchtemplate = SearchTemplate.query.get(template_id) + searchtemplate = SearchTemplate.get_by_id(template_id) # Copy values from the template view_name = searchtemplate.name @@ -131,7 +131,7 @@ def get(self, sketch_id): Returns: Views in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -158,7 +158,7 @@ def post(self, sketch_id): "Unable to save view, not able to validate form data.", ) - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -190,10 +190,10 @@ def get(self, sketch_id, view_id): Returns: A view in JSON (instance of flask.wrappers.Response) """ - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") - view = View.query.get(view_id) + view = View.get_by_id(view_id) if not sketch.has_permission(current_user, "read"): abort( @@ -238,7 +238,7 @@ def delete(self, sketch_id, view_id): 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) + sketch = Sketch.get_with_acl(sketch_id) if not sketch: abort(HTTP_STATUS_CODE_NOT_FOUND, "No sketch found with this ID.") @@ -247,7 +247,7 @@ def delete(self, sketch_id, view_id): HTTP_STATUS_CODE_FORBIDDEN, "User does not have write access controls on sketch.", ) - view = View.query.get(view_id) + view = View.get_by_id(view_id) if not view: abort(HTTP_STATUS_CODE_NOT_FOUND, "No view found with this ID.") @@ -289,7 +289,7 @@ def post(self, sketch_id, view_id): HTTP_STATUS_CODE_BAD_REQUEST, "Unable to update view, not able to validate form data", ) - sketch = Sketch.query.get_with_acl(sketch_id) + sketch = Sketch.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"): @@ -298,7 +298,7 @@ def post(self, sketch_id, view_id): "User does not have write access controls on sketch.", ) - view = View.query.get(view_id) + view = View.get_by_id(view_id) if not view: abort(HTTP_STATUS_CODE_NOT_FOUND, "No view found with this ID.") diff --git a/timesketch/app.py b/timesketch/app.py index 901e565448..a5851bf5f1 100644 --- a/timesketch/app.py +++ b/timesketch/app.py @@ -176,7 +176,7 @@ def load_user(user_id): Returns: A user object (Instance of timesketch.models.user.User). """ - return User.query.get(user_id) + return User.session.get(User, user_id) # Setup CSRF protection for the whole application CSRFProtect(app) diff --git a/timesketch/lib/aggregators/interface.py b/timesketch/lib/aggregators/interface.py index 261e956283..3a606578c1 100644 --- a/timesketch/lib/aggregators/interface.py +++ b/timesketch/lib/aggregators/interface.py @@ -186,7 +186,7 @@ def __init__(self, sketch_id=None, indices=None, timeline_ids=None): self._sketch_url = "/sketch/{0:d}/explore".format(sketch_id) self.field = "" self.indices = indices - self.sketch = SQLSketch.query.get(sketch_id) + self.sketch = SQLSketch.get_by_id(sketch_id) self.timeline_ids = None active_timelines = self.sketch.active_timelines diff --git a/timesketch/lib/analyzers/evtx_sessionizers_test.py b/timesketch/lib/analyzers/evtx_sessionizers_test.py index 50635fb385..6ecf499fc8 100644 --- a/timesketch/lib/analyzers/evtx_sessionizers_test.py +++ b/timesketch/lib/analyzers/evtx_sessionizers_test.py @@ -60,8 +60,8 @@ class TestWinEXTXSessionizerPlugin(BaseTest): @mock.patch("timesketch.lib.analyzers.interface.OpenSearchDataStore", MockDataStore) def test_get_event_data(self): """Test getEventData returns the correct values.""" - user = User("test_user") - sketch = Sketch("test_sketch", "description", user) + user = User(username="test_user", name="test user") + sketch = Sketch(name="test_sketch", description="description", user=user) label = sketch.Label(label="Test label", user=user) sketch.labels.append(label) diff --git a/timesketch/lib/analyzers/interface.py b/timesketch/lib/analyzers/interface.py index 5b0f96602c..ca7cb30e1f 100644 --- a/timesketch/lib/analyzers/interface.py +++ b/timesketch/lib/analyzers/interface.py @@ -382,7 +382,7 @@ def __init__(self, sketch_id, analyzer=None): sketch_id: The Sketch ID. """ self.id = sketch_id - self.sql_sketch = SQLSketch.query.get(sketch_id) + self.sql_sketch = SQLSketch.get_by_id(sketch_id) self._analyzer = analyzer if not self.sql_sketch: @@ -416,7 +416,7 @@ def add_aggregation( raise ValueError("Aggregator parameters have to be defined.") if view_id: - view = View.query.get(view_id) + view = View.get_by_id(view_id) else: view = None @@ -454,7 +454,7 @@ def add_aggregation_group(self, name, description="", view_id=None): raise ValueError("Aggregator group name needs to be defined.") if view_id: - view = View.query.get(view_id) + view = View.get_by_id(view_id) else: view = None @@ -1115,7 +1115,7 @@ def run_wrapper(self, analysis_id): Returns: Return value of the run method. """ - analysis = Analysis.query.get(analysis_id) + analysis = Analysis.get_by_id(analysis_id) analysis.set_status("STARTED") timeline = analysis.timeline diff --git a/timesketch/lib/stories/api_fetcher.py b/timesketch/lib/stories/api_fetcher.py index 308051b169..70954cfdcc 100644 --- a/timesketch/lib/stories/api_fetcher.py +++ b/timesketch/lib/stories/api_fetcher.py @@ -58,7 +58,7 @@ def get_aggregation(self, agg_dict): if not aggregation_id: return {} - aggregation = Aggregation.query.get(aggregation_id) + aggregation = Aggregation.get_by_id(aggregation_id) if not aggregation: return {} @@ -113,7 +113,7 @@ def get_aggregation_group(self, agg_dict): if not group_id: return None - group = AggregationGroup.query.get(group_id) + group = AggregationGroup.get_by_id(group_id) if not group: return None @@ -210,7 +210,7 @@ def get_view(self, view_dict): if not view_id: return pd.DataFrame() - view = View.query.get(view_id) + view = View.get_by_id(view_id) if not view: return pd.DataFrame() @@ -228,7 +228,7 @@ def get_view(self, view_dict): else: query_dsl = None - sketch = Sketch.query.get_with_acl(self._sketch_id) + sketch = Sketch.get_with_acl(self._sketch_id) sketch_indices = [t.searchindex.index_name for t in sketch.active_timelines] results = self._datastore.search_stream( diff --git a/timesketch/lib/tasks.py b/timesketch/lib/tasks.py index 68168792df..f6e3b6c099 100644 --- a/timesketch/lib/tasks.py +++ b/timesketch/lib/tasks.py @@ -170,7 +170,7 @@ def _set_timeline_status(timeline_id, status, error_msg=None): Args: timeline_id: Timeline ID. """ - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) if not timeline: logger.warning("Cannot set status: No such timeline") return @@ -196,7 +196,7 @@ def _set_timeline_status(timeline_id, status, error_msg=None): def _set_datasource_status(timeline_id, file_path, status, error_message=None): - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) for datasource in timeline.datasources: if datasource.get_file_on_disk == file_path: datasource.set_status(status) @@ -211,7 +211,7 @@ def _set_datasource_status(timeline_id, file_path, status, error_message=None): def _set_datasource_total_events(timeline_id, file_path, total_file_events): - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) for datasource in timeline.datasources: if datasource.get_file_on_disk == file_path: datasource.set_total_file_events(total_file_events) @@ -369,21 +369,22 @@ def build_sketch_analysis_pipeline( analyzer_kwargs = current_app.config.get("ANALYZERS_DEFAULT_KWARGS", {}) if user_id: - user = User.query.get(user_id) + user = User.get_by_id(user_id) else: user = None - sketch = Sketch.query.get(sketch_id) - analysis_session = AnalysisSession(user, sketch) + sketch = Sketch.get_by_id(sketch_id) + analysis_session = AnalysisSession(user=user, sketch=sketch) + db_session.add(analysis_session) analyzers = manager.AnalysisManager.get_analyzers(analyzer_names) for analyzer_name, analyzer_class in analyzers: base_kwargs = analyzer_kwargs.get(analyzer_name, {}) - searchindex = SearchIndex.query.get(searchindex_id) + searchindex = SearchIndex.get_by_id(searchindex_id) timeline = None if timeline_id: - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) if not timeline: timeline = Timeline.query.filter_by( @@ -438,8 +439,8 @@ def build_sketch_analysis_pipeline( ) analysis.add_attribute(name="kwargs_hash", value=kwargs_list_hash) analysis.set_status("PENDING") - analysis_session.analyses.append(analysis) db_session.add(analysis) + analysis_session.analyses.append(analysis) db_session.commit() tasks.append( @@ -511,7 +512,7 @@ def run_email_result_task(index_name, sketch_id=None): return "" if sketch_id: - sketch = Sketch.query.get(sketch_id) + sketch = Sketch.get_by_id(sketch_id) subject = "Timesketch: [{0:s}] is ready".format(searchindex.name) @@ -969,7 +970,7 @@ def find_data_task( data_finder.set_rule(data_finder_dict.get(rule_name)) data_finder.set_timeline_ids(timeline_ids) - sketch = Sketch.query.get(sketch_id) + sketch = Sketch.get_by_id(sketch_id) indices = set() for timeline in sketch.active_timelines: if timeline.id not in timeline_ids: diff --git a/timesketch/lib/testlib.py b/timesketch/lib/testlib.py index 535988ead3..545e0d9c14 100644 --- a/timesketch/lib/testlib.py +++ b/timesketch/lib/testlib.py @@ -443,7 +443,7 @@ def _create_user(self, username, set_password=False): Returns: A user (instance of timesketch.models.user.User) """ - user = User.get_or_create(username=username) + user = User.get_or_create(username=username, name=username) if set_password: user.set_password(plaintext="test", rounds=4) self._commit_to_database(user) @@ -457,7 +457,7 @@ def _create_group(self, name, user): Returns: A group (instance of timesketch.models.user.Group) """ - group = Group.get_or_create(name=name) + group = Group.get_or_create(name=name, display_name=name, description=name) user.groups.append(group) self._commit_to_database(group) return group @@ -701,7 +701,7 @@ class ModelBaseTest(BaseTest): def _test_db_object(self, expected_result=None, model_cls=None): """Generic test that checks if the stored data is correct.""" - db_obj = model_cls.query.get(1) + db_obj = model_cls.get_by_id(1) for x in expected_result: k, v = x[0], x[1] self.assertEqual(db_obj.__getattribute__(k), v) diff --git a/timesketch/models/__init__.py b/timesketch/models/__init__.py index 207797181c..cc314f1175 100644 --- a/timesketch/models/__init__.py +++ b/timesketch/models/__init__.py @@ -17,10 +17,9 @@ from flask import abort from flask_login import current_user -from flask_sqlalchemy import BaseQuery +from flask_sqlalchemy.query import Query from sqlalchemy import create_engine -from sqlalchemy.orm import scoped_session, sessionmaker -from sqlalchemy.ext.declarative import as_declarative +from sqlalchemy.orm import scoped_session, sessionmaker, as_declarative from sqlalchemy.ext.declarative import declared_attr from sqlalchemy import Column from sqlalchemy import DateTime @@ -32,7 +31,7 @@ # The database session engine = None -session_maker = sessionmaker() +session_maker = sessionmaker(future=True) db_session = scoped_session(session_maker) @@ -42,10 +41,10 @@ def configure_engine(url): # pylint: disable=global-statement,global-variable-not-assigned # TODO: Can we wrap this in a class? global engine, session_maker, db_session - engine = create_engine(url) - # Set the query class to our own AclBaseQuery + engine = create_engine(url, future=True) + # Configure the session session_maker.configure( - autocommit=False, autoflush=False, bind=engine, query_cls=AclBaseQuery + autocommit=False, autoflush=False, bind=engine, query_cls=Query ) @@ -55,6 +54,7 @@ def init_db(): """ BaseModel.metadata.create_all(bind=engine) BaseModel.query = db_session.query_property() + BaseModel.session = db_session return BaseModel @@ -63,13 +63,26 @@ def drop_all(): BaseModel.metadata.drop_all(bind=engine) -class AclBaseQuery(BaseQuery): - """The query object used for models. It subclasses - flask_sqlalchemy.BaseQuery and has the same methods as a SQLAlchemy Query - as well. +@as_declarative() +class BaseModel(object): + """Base class used for database models. It adds common model fields to all + models classes that subclass it. """ - def get_with_acl(self, model_id, user=current_user): + @declared_attr + def __tablename__(self): + return self.__name__.lower() + + id = Column(Integer, primary_key=True) + created_at = Column(DateTime(), default=func.now()) + updated_at = Column(DateTime(), default=func.now(), onupdate=func.now()) + + def update_modification_time(self): + """Set updated_at field to current time.""" + self.updated_at = func.now() + + @classmethod + def get_with_acl(cls, model_id, user=current_user): """Get a database object with permission check enforced. Args: @@ -79,7 +92,7 @@ def get_with_acl(self, model_id, user=current_user): Returns: A BaseQuery instance. """ - result_obj = self.get(model_id) + result_obj = db_session.get(cls, model_id) if not result_obj: abort(HTTP_STATUS_CODE_NOT_FOUND) try: @@ -93,24 +106,17 @@ def get_with_acl(self, model_id, user=current_user): abort(HTTP_STATUS_CODE_FORBIDDEN) return result_obj + @classmethod + def get_by_id(cls, model_id): + """Get model instance by id. -@as_declarative() -class BaseModel(object): - """Base class used for database models. It adds common model fields to all - models classes that subclass it. - """ - - @declared_attr - def __tablename__(self): - return self.__name__.lower() - - id = Column(Integer, primary_key=True) - created_at = Column(DateTime(), default=func.now()) - updated_at = Column(DateTime(), default=func.now(), onupdate=func.now()) + Args: + model_id: The integer ID of the model to get. - def update_modification_time(self): - """Set updated_at field to current time.""" - self.updated_at = func.now() + Returns: + A model instance. + """ + return db_session.get(cls, model_id) @classmethod def get_or_create(cls, **kwargs): diff --git a/timesketch/models/acl.py b/timesketch/models/acl.py index e07ba86b84..a33d7852d5 100644 --- a/timesketch/models/acl.py +++ b/timesketch/models/acl.py @@ -127,7 +127,7 @@ def all_with_acl(cls, user=None): user: A user (Instance of timesketch.models.user.User) Returns: - An ACL base query (instance of timesketch.models.AclBaseQuery) + A SQLAlchemy query (instance of sqlalchemy.orm.query.Query) """ # If no user, assume the user that made the request. if not user: @@ -343,12 +343,14 @@ def grant_permission(self, permission, user=None, group=None): # Grant permission to a group. if group and not self._get_ace(permission, group=group): self.acl.append(self.AccessControlEntry(permission=permission, group=group)) + db_session.add(self) db_session.commit() return # Grant permission to a user. if not self._get_ace(permission, user=user, check_group=False): self.acl.append(self.AccessControlEntry(permission=permission, user=user)) + db_session.add(self) db_session.commit() def revoke_permission(self, permission, user=None, group=None): @@ -365,6 +367,7 @@ def revoke_permission(self, permission, user=None, group=None): if group_ace: for ace in group_ace: self.acl.remove(ace) + db_session.add(self) db_session.commit() return @@ -373,4 +376,5 @@ def revoke_permission(self, permission, user=None, group=None): if user_ace: for ace in user_ace: self.acl.remove(ace) + db_session.add(self) db_session.commit() diff --git a/timesketch/models/annotations.py b/timesketch/models/annotations.py index 6ce1fb9140..aa38f81ac4 100644 --- a/timesketch/models/annotations.py +++ b/timesketch/models/annotations.py @@ -59,51 +59,18 @@ class Label(BaseAnnotation): label = Column(Unicode(255)) - def __init__(self, user, label): - """Initialize the model. - - Args: - user: A user (instance of timesketch.models.user.User) - name: Name of the label - """ - super().__init__() - self.user = user - self.label = label - class Comment(BaseAnnotation): """A comment annotation.""" comment = Column(UnicodeText()) - def __init__(self, user, comment): - """Initialize the model. - - Args: - user: A user (instance of timesketch.models.user.User) - body: The body if the comment - """ - super().__init__() - self.user = user - self.comment = comment - class Status(BaseAnnotation): """A status annotation.""" status = Column(Unicode(255)) - def __init__(self, user, status): - """Initialize the model. - - Args: - user: A user (instance of timesketch.models.user.User) - status: The type of status (string, e.g. open) - """ - super().__init__() - self.user = user - self.status = status - class GenericAttribute(BaseAnnotation): """Implements the attribute model.""" @@ -113,23 +80,6 @@ class GenericAttribute(BaseAnnotation): ontology = Column(UnicodeText()) description = Column(UnicodeText()) - def __init__(self, user, name, value, ontology, description): - """Initialize the Attribute object. - - Args: - user: A user (instance of timesketch.models.user.User) - name (str): The name of the attribute. - value (str): The value of the attribute - ontology (str): The ontology (type) of the value, The values that - can be used are defined in timesketch/lib/ontology.py. - """ - super().__init__() - self.user = user - self.name = name - self.value = value - self.ontology = ontology - self.description = description - class LabelMixin(object): """ @@ -180,6 +130,7 @@ def add_label(self, label, user=None): if self.has_label(label): return self.labels.append(self.Label(user=user, label=label)) + db_session.add(self) db_session.commit() def remove_label(self, label): @@ -192,6 +143,7 @@ def remove_label(self, label): if label_obj.label.lower() != label.lower(): continue self.labels.remove(label_obj) + db_session.add(self) db_session.commit() def has_label(self, label): @@ -280,6 +232,7 @@ def remove_comment(self, comment_id): for comment_obj in self.comments: if comment_obj.id == int(comment_id): self.comments.remove(comment_obj) + db_session.add(self) db_session.commit() return True @@ -308,6 +261,7 @@ def update_comment(self, comment_id, comment): for comment_obj in self.comments: if comment_obj.id == int(comment_id): comment_obj.comment = comment + db_session.add(self) db_session.commit() return comment_obj @@ -362,6 +316,7 @@ def set_status(self, status): for _status in self.status: self.status.remove(_status) self.status.append(self.Status(user=None, status=status)) + db_session.add(self) db_session.commit() @property @@ -428,6 +383,7 @@ def add_attribute(self, name, value, ontology=None, user=None, description=None) description=description, ) ) + db_session.add(self) db_session.commit() @property diff --git a/timesketch/models/sigma.py b/timesketch/models/sigma.py index 1ae2c2c7d0..e26fd63d25 100644 --- a/timesketch/models/sigma.py +++ b/timesketch/models/sigma.py @@ -43,29 +43,4 @@ class SigmaRule( description = Column(UnicodeText()) rule_uuid = Column(Unicode(255), unique=True) rule_yaml = Column(UnicodeText()) - user_id = Column( - Integer, ForeignKey("user.id") - ) # who added the rule to the system (TS user) - - def __init__( - self, - user, - title=None, - description=None, - rule_uuid=None, - rule_yaml=None, - ): - """Initialize the SigmaRule object. - Args: - user: A user (instance of timesketch.models.user.User) - title: Title for the rule - description: description of the rule - rule_uuid: uuid of the rule - rule_yaml: yaml content of the rule - """ - super().__init__() - self.user = user - self.rule_uuid = rule_uuid - self.rule_yaml = rule_yaml - self.title = title - self.description = description + user_id = Column(Integer, ForeignKey("user.id")) diff --git a/timesketch/models/sketch.py b/timesketch/models/sketch.py index c44f2b99a0..53544b4baf 100644 --- a/timesketch/models/sketch.py +++ b/timesketch/models/sketch.py @@ -74,19 +74,6 @@ class Sketch(AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, BaseMode searchhistories = relationship("SearchHistory", backref="sketch", lazy="dynamic") scenarios = relationship("Scenario", backref="sketch", lazy="dynamic") - def __init__(self, name, description, user): - """Initialize the Sketch object. - - Args: - name: The name of the sketch - description: Description of the sketch - user: A user (instance of timesketch.models.user.User) - """ - super().__init__() - self.name = name - self.description = description - self.user = user - @property def get_named_aggregations(self): """Get named aggregations that don't belong to a group. @@ -205,45 +192,13 @@ class Timeline(LabelMixin, StatusMixin, CommentMixin, BaseModel): name = Column(Unicode(255)) description = Column(UnicodeText()) - color = Column(Unicode(6)) + color = Column(Unicode(6), default=random_color()) user_id = Column(Integer, ForeignKey("user.id")) searchindex_id = Column(Integer, ForeignKey("searchindex.id")) sketch_id = Column(Integer, ForeignKey("sketch.id")) analysis = relationship("Analysis", backref="timeline", lazy="select") datasources = relationship("DataSource", backref="timeline", lazy="select") - def __init__( - self, - name, - user, - sketch, - searchindex, - color=None, - description=None, - ): - """Initialize the Timeline object. - - Args: - name: The name of the timeline - user: A user (instance of timesketch.models.user.User) - sketch: A sketch (instance of timesketch.models.sketch.Sketch) - searchindex: A searchindex - (instance of timesketch.models.sketch.SearchIndex) - color: Color for the timeline in HEX as string (e.g. F1F1F1F1) - description: The description for the timeline - """ - super().__init__() - self.name = name - self.description = description - - if not color: - color = random_color() - - self.color = color - self.user = user - self.sketch = sketch - self.searchindex = searchindex - class SearchIndex(AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, BaseModel): """Implements the SearchIndex model.""" @@ -255,21 +210,6 @@ class SearchIndex(AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, Bas timelines = relationship("Timeline", backref="searchindex", lazy="dynamic") events = relationship("Event", backref="searchindex", lazy="dynamic") - def __init__(self, name, description, index_name, user): - """Initialize the SearchIndex object. - - Args: - name: The name of the timeline - description: The description for the timeline - index_name: The name of the searchindex - user: A user (instance of timesketch.models.user.User) - """ - super().__init__() - self.name = name - self.description = description - self.index_name = index_name - self.user = user - class View(AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, BaseModel): """Implements the View model.""" @@ -286,42 +226,6 @@ class View(AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, BaseModel) aggregations = relationship("Aggregation", backref="view", lazy="select") aggregationgroups = relationship("AggregationGroup", backref="view", lazy="select") - def __init__( - self, - name, - sketch, - user, - description=None, - searchtemplate=None, - query_string=None, - query_filter=None, - query_dsl=None, - searchtemplate_json=None, - ): - """Initialize the View object. - - Args: - name: The name of the timeline - sketch: A sketch (instance of timesketch.models.sketch.Sketch) - user: A user (instance of timesketch.models.user.User) - description (str): Description of the view - searchtemplate: Instance of timesketch.models.sketch.SearchTemplate - query_string: The query string - query_filter: The filter to apply (JSON format as string) - query_dsl: A query DSL document (JSON format as string) - searchtemplate_json: The search template used (JSON format as string) - """ - super().__init__() - self.name = name - self.sketch = sketch - self.user = user - self.description = description - self.searchtemplate = searchtemplate - self.query_string = query_string - self.query_filter = query_filter - self.query_dsl = query_dsl - self.searchtemplate_json = searchtemplate_json - def validate_filter(self, query_filter=None): """Validate the Query Filter. @@ -377,60 +281,11 @@ class SearchTemplate( query_string = Column(UnicodeText()) query_filter = Column(UnicodeText()) query_dsl = Column(UnicodeText()) - template_uuid = Column(Unicode(255), unique=True) + template_uuid = Column(Unicode(255), unique=True, default=str(uuid4())) template_json = Column(UnicodeText()) user_id = Column(Integer, ForeignKey("user.id")) views = relationship("View", backref="searchtemplate", lazy="select") - def __init__( - self, - name, - user=None, - short_name=None, - description=None, - query_string=None, - query_filter=None, - query_dsl=None, - template_uuid=None, - template_json=None, - ): - """Initialize the Search Template object. - - Args: - name: The human readable name of the template - user: A user (instance of timesketch.models.user.User) - short_name: The name of the template (snake case) - description (str): Description of the search template - query_string: The query string - query_filter: The filter to apply (JSON format as string) - query_dsl: A query DSL document (JSON format as string) - template_uuid: UUID of the template - template_json: Specification of the template (JSON format as string) - """ - super().__init__() - self.name = name - self.user = user - if not short_name: - short_name = name.replace(" ", "_").lower() - self.short_name = short_name - self.description = description - self.query_string = query_string - if not query_filter: - filter_template = { - "exclude": [], - "indices": "_all", - "terminate_after": 40, - "order": "asc", - "size": "40", - } - query_filter = json.dumps(filter_template, ensure_ascii=False) - self.query_filter = query_filter - self.query_dsl = query_dsl - if not template_uuid: - template_uuid = str(uuid4()) - self.template_uuid = template_uuid - self.template_json = template_json - class Event(LabelMixin, StatusMixin, CommentMixin, BaseModel): """Implements the Event model.""" @@ -439,20 +294,6 @@ class Event(LabelMixin, StatusMixin, CommentMixin, BaseModel): searchindex_id = Column(Integer, ForeignKey("searchindex.id")) document_id = Column(Unicode(255)) - def __init__(self, sketch, searchindex, document_id): - """Initialize the Event object. - - Args: - sketch: A sketch (instance of timesketch.models.sketch.Sketch) - searchindex: A searchindex - (instance of timesketch.models.sketch.SearchIndex) - document_id = String with the datastore document ID - """ - super().__init__() - self.sketch = sketch - self.searchindex = searchindex - self.document_id = document_id - class Story(AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, BaseModel): """Implements the Story model.""" @@ -462,21 +303,6 @@ class Story(AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, BaseModel user_id = Column(Integer, ForeignKey("user.id")) sketch_id = Column(Integer, ForeignKey("sketch.id")) - def __init__(self, title, content, sketch, user): - """Initialize the Story object. - - Args: - title: The title of the story - content: Content of the story - sketch: A sketch (instance of timesketch.models.sketch.Sketch) - user: A user (instance of timesketch.models.user.User) - """ - super().__init__() - self.title = title - self.content = content - self.sketch = sketch - self.user = user - class Aggregation(AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, BaseModel): """Implements the Aggregation model.""" @@ -491,43 +317,6 @@ class Aggregation(AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, Bas view_id = Column(Integer, ForeignKey("view.id")) aggregationgroup_id = Column(Integer, ForeignKey("aggregationgroup.id")) - def __init__( - self, - name, - description, - agg_type, - parameters, - chart_type, - user, - sketch, - view=None, - aggregationgroup=None, - ): - """Initialize the Aggregation object. - - Args: - name (str): Name of the aggregation - description (str): Description of the aggregation - agg_type (str): Aggregation plugin type - parameters (str): JSON serialized dict with aggregation parameters - chart_type (str): Chart plugin type - user (User): The user who created the aggregation - sketch (Sketch): The sketch that the aggregation is bound to - view (View): Optional, the view that the aggregation is bound to - aggregationgroup (AggregationGroup): Optional, an AggregationGroup - that the aggregation is bound to. - """ - super().__init__() - self.name = name - self.description = description - self.agg_type = agg_type - self.aggregationgroup = aggregationgroup - self.parameters = parameters - self.chart_type = chart_type - self.user = user - self.sketch = sketch - self.view = view - class AggregationGroup( AccessControlMixin, LabelMixin, StatusMixin, CommentMixin, BaseModel @@ -545,40 +334,6 @@ class AggregationGroup( sketch_id = Column(Integer, ForeignKey("sketch.id")) view_id = Column(Integer, ForeignKey("view.id")) - def __init__( - self, - name, - description, - user, - sketch, - aggregations=None, - parameters="", - orientation="", - view=None, - ): - """Initialize the AggregationGroup object. - - Args: - name (str): Name of the aggregation - description (str): Description of the aggregation - user (User): The user who created the aggregation - sketch (Sketch): The sketch that the aggregation is bound to - aggregations (Aggregation): List of aggregation objects. - parameters (str): A JSON formatted dict with parameters for - charting. - orientation (str): Describes how charts should be joined together. - view (View): Optional: The view that the aggregation is bound to - """ - super().__init__() - self.name = name - self.description = description - self.aggregations = aggregations or [] - self.parameters = parameters - self.orientation = orientation - self.user = user - self.sketch = sketch - self.view = view - class Analysis(GenericAttributeMixin, LabelMixin, StatusMixin, CommentMixin, BaseModel): """Implements the analysis model.""" @@ -595,43 +350,6 @@ class Analysis(GenericAttributeMixin, LabelMixin, StatusMixin, CommentMixin, Bas timeline_id = Column(Integer, ForeignKey("timeline.id")) searchindex_id = Column(Integer, ForeignKey("searchindex.id")) - def __init__( - self, - name, - description, - analyzer_name, - parameters, - user, - sketch, - timeline=None, - searchindex=None, - result=None, - ): - """Initialize the Analysis object. - - Args: - name (str): Name of the analysis - description (str): Description of the analysis - analyzer_name (str): Name of the analyzer - parameters (str): JSON serialized dict with analyser parameters - user (User): The user who created the aggregation - sketch (Sketch): The sketch that the aggregation is bound to - timeline (Timeline): Timeline the analysis was run on - searchindex (SearchIndex): SearchIndex the analysis was run on - result (str): Result report of the analysis - """ - super().__init__() - self.name = name - self.description = description - self.analyzer_name = analyzer_name - self.parameters = parameters - self.user = user - self.sketch = sketch - self.timeline = timeline - self.searchindex = searchindex - self.result = result - self.log = "" - class AnalysisSession(LabelMixin, StatusMixin, CommentMixin, BaseModel): """Implements the analysis session model.""" @@ -640,17 +358,6 @@ class AnalysisSession(LabelMixin, StatusMixin, CommentMixin, BaseModel): sketch_id = Column(Integer, ForeignKey("sketch.id")) analyses = relationship("Analysis", backref="analysissession", lazy="select") - def __init__(self, user, sketch): - """Initialize the AnalysisSession object. - - Args: - user (User): The user who created the aggregation - sketch (Sketch): The sketch that the aggregation is bound to - """ - super().__init__() - self.user = user - self.sketch = sketch - class Attribute(BaseModel): """Implements the attribute model.""" @@ -661,22 +368,6 @@ class Attribute(BaseModel): ontology = Column(UnicodeText()) values = relationship("AttributeValue", backref="attribute", lazy="select") - def __init__(self, user, sketch, name, ontology): - """Initialize the Attribute object. - - Args: - user (User): The user who created the attribute - sketch (Sketch): The sketch that the attribute is bound to - name (str): the name of the attribute. - ontology (str): The ontology of the value, The values that can - be used are defined in timesketch/lib/ontology.py (ONTOLOGY). - """ - super().__init__() - self.user = user - self.sketch = sketch - self.name = name - self.ontology = ontology - class AttributeValue(BaseModel): """Implements the attribute value model.""" @@ -685,21 +376,6 @@ class AttributeValue(BaseModel): attribute_id = Column(Integer, ForeignKey("attribute.id")) value = Column(UnicodeText()) - def __init__(self, user, attribute, value): - """Initialize the Attribute value object. - - Args: - user (User): The user who created the attribute value. - attribute (Attribute): The attribute this value is bound to. - value (str): a string that contains the value for the attribute. - The ontology could influence how this will be cast when - interpreted. - """ - super().__init__() - self.user = user - self.attribute = attribute - self.value = value - class Graph(LabelMixin, CommentMixin, BaseModel): """Implements the graph model.""" @@ -714,42 +390,6 @@ class Graph(LabelMixin, CommentMixin, BaseModel): num_nodes = Column(Integer) num_edges = Column(Integer) - def __init__( - self, - user, - sketch, - name, - description=None, - graph_config=None, - graph_elements=None, - graph_thumbnail=None, - num_nodes=None, - num_edges=None, - ): - """Initialize the Graph object. - - Args: - user (User): The user who created the graph. - sketch (Sketch): The sketch that the graph is bound to. - name (str): Name of the graph. - description (str): Description of the graph. - graph_config (dict): Config used when generating the graph. - graph_elements (str): Graph in json string format. - graph_thumbnail (str): Image of graph in Base64 format. - num_nodes (int): Number of nodes in the graph. - num_edges (int): Number of edges in the graph. - """ - super().__init__() - self.user = user - self.sketch = sketch - self.name = name - self.description = description - self.graph_config = graph_config - self.graph_elements = graph_elements - self.graph_thumbnail = graph_thumbnail - self.num_nodes = num_nodes - self.num_edges = num_edges - class GraphCache(BaseModel): """Implements the graph cache model.""" @@ -761,33 +401,6 @@ class GraphCache(BaseModel): num_nodes = Column(Integer) num_edges = Column(Integer) - def __init__( - self, - sketch, - graph_plugin=None, - graph_config=None, - graph_elements=None, - num_nodes=None, - num_edges=None, - ): - """Initialize the GraphCache object. - - Args: - sketch (Sketch): The sketch that the graph is bound to. - graph_plugin (str): Name of the graph plugin that was used. - graph_config (dict): Config used when generating the graph. - graph_elements (str): Graph in json string format. - num_nodes (int): Number of nodes in the graph. - num_edges (int): Number of edges in the graph. - """ - super().__init__() - self.sketch = sketch - self.graph_plugin = graph_plugin - self.graph_config = graph_config - self.graph_elements = graph_elements - self.num_nodes = num_nodes - self.num_edges = num_edges - class DataSource(LabelMixin, StatusMixin, CommentMixin, BaseModel): """Implements the datasource model.""" @@ -800,54 +413,17 @@ class DataSource(LabelMixin, StatusMixin, CommentMixin, BaseModel): file_size = Column(BigInteger()) original_filename = Column(UnicodeText()) data_label = Column(UnicodeText()) - error_message = Column(UnicodeText()) - total_file_events = Column(BigInteger()) - - def __init__( - self, - timeline, - user, - provider, - context, - file_on_disk, - file_size, - original_filename, - data_label, - error_message="", - total_file_events=0, - ): # pylint: disable=too-many-arguments - """Initialize the DataSource object. - - Args: - timeline (Timeline): Timeline that this datasource is part of. - user (User): The user who imported the data. - provider (str): Name of the application that collected the data. - context (str): Context on how the data was collected. - file_on_disk (str): Path to uploaded file. - file_size (int): Size on disk for uploaded file. - original_filename (str): Original filename for uploaded file. - data_label (str): Data label for the uploaded data. - error_message (str): Optional error message in case the data source - did not successfully import. - """ - super().__init__() - self.timeline = timeline - self.user = user - self.provider = provider - self.context = context - self.file_on_disk = file_on_disk - self.file_size = file_size - self.original_filename = original_filename - self.data_label = data_label - self.error_message = error_message - self.total_file_events = total_file_events + error_message = Column(UnicodeText(), default="") + total_file_events = Column(BigInteger(), default=0) def set_total_file_events(self, total_file_events): self.total_file_events = total_file_events + db_session.add(self) db_session.commit() def set_error_message(self, error_message): self.error_message = error_message + db_session.add(self) db_session.commit() @property @@ -899,35 +475,6 @@ class SearchHistory(LabelMixin, BaseModel): collection_class=attribute_mapped_collection("id"), ) - def __init__( - self, - user, - sketch, - description=None, - query_string=None, - query_filter=None, - query_dsl=None, - parent=None, - ): - """ "Initialize the SearchHistory object - - Args: - user (User): The user who owns the search history. - sketch (Sketch): The sketch for the search history. - description (str): Description for the search history entry. - query_string (str): The query string. - query_filter (str): The filter to apply (JSON format as string). - query_dsl (str): A query DSL document (JSON format as string). - parent (SearchHistory): Reference to parent search history entry. - """ - self.user = user - self.sketch = sketch - self.description = description - self.query_string = query_string - self.query_filter = query_filter - self.query_dsl = query_dsl - self.parent = parent - @staticmethod def build_node_dict(node_dict, node): node_dict["id"] = node.id @@ -992,36 +539,6 @@ class Scenario(LabelMixin, StatusMixin, CommentMixin, GenericAttributeMixin, Bas facets = relationship("Facet", backref="scenario", lazy="select") search_histories = relationship("SearchHistory", backref="scenario", lazy="select") - def __init__( - self, - name, - display_name, - dfiq_identifier, - sketch, - user, - spec_json, - description=None, - ): - """Initialize the Scenario object. - - Args: - name (str): The name of the scenario - display_name (str): The display name of the scenario - dfiq_identifier (str): DFIQ identifier for scenario - sketch (timesketch.models.sketch.Sketch): A sketch - user (timesketch.models.user.User): A user - spec_json (str): Scenario specification from YAML - description (str): Description of the scenario - """ - super().__init__() - self.name = name - self.display_name = display_name - self.dfiq_identifier = dfiq_identifier - self.sketch = sketch - self.user = user - self.spec_json = spec_json - self.description = description - class FacetTimeFrame(BaseModel): """Implements the FacetTimeFrame model. @@ -1036,22 +553,6 @@ class FacetTimeFrame(BaseModel): user_id = Column(Integer, ForeignKey("user.id")) facet_id = Column(Integer, ForeignKey("facet.id")) - def __init__(self, start_time, end_time, facet, user=None, description=None): - """Initialize the InvestigationTimeFrame object. - - Args: - start_time (datetime): Timezone-aware UTC datetime object. - end_time (datetime): Timezone-aware UTC datetime object. - facet (Facet): Facet for this time frame - description (str): Description of the timeframe (optional) - """ - super().__init__() - self.start_time = start_time - self.end_time = end_time - self.facet = facet - self.user = user - self.description = description - # Association tables for the many-to-many relationship for a conclusion. facetconclusion_story_association_table = Table( @@ -1108,21 +609,6 @@ class FacetConclusion(LabelMixin, StatusMixin, CommentMixin, BaseModel): "Aggregation", secondary=facetconclusion_aggregation_association_table ) - def __init__(self, conclusion, user, facet, automated=False): - """Initialize the InvestigationConclusion object. - - Args: - conclusion (str): The conclusion of the investigation - user (User): A user - facet (Facet): Facet for this conclusion - automated (bool): Indicate if conclusion was automated - """ - super().__init__() - self.conclusion = conclusion - self.user = user - self.facet = facet - self.automated = automated - # Association table for the many-to-many relationship for timelines in an # investigation. @@ -1157,28 +643,6 @@ class Facet(LabelMixin, StatusMixin, CommentMixin, GenericAttributeMixin, BaseMo conclusions = relationship("FacetConclusion", backref="facet", lazy="select") search_histories = relationship("SearchHistory", backref="facet", lazy="select") - def __init__( - self, name, display_name, dfiq_identifier, user, spec_json, description=None - ): - """Initialize the Facet object. - - Args: - name (str): The name of the investigation - display_name (str): The display name of the investigation - dfiq_identifier (str): DFIQ identifier for facet - user (User): A userinvestigationconclusion - scenario (Scenario): The Scenario this investigation belongs to - spec_json (str): Investigation specification - description (str): Description of the investigation - """ - super().__init__() - self.name = name - self.display_name = display_name - self.dfiq_identifier = dfiq_identifier - self.user = user - self.spec_json = spec_json - self.description = description - # Association tables for the many-to-many relationship for a question conclusion. questionconclusion_story_association_table = Table( @@ -1253,21 +717,6 @@ class InvestigativeQuestionConclusion(LabelMixin, StatusMixin, CommentMixin, Bas "Aggregation", secondary=questionconclusion_aggregation_association_table ) - def __init__(self, user, investigativequestion, conclusion=None, automated=False): - """Initialize the QuestionConclusion object. - - Args: - conclusion (str): The conclusion of the question - user (timesketch.models.user.User): A user - investigativequestion (InvestigativeQuestion): A question - automated (bool): Indicate if conclusion was automated - """ - super().__init__() - self.user = user - self.investigativequestion = investigativequestion - self.conclusion = conclusion - self.automated = automated - class InvestigativeQuestion( LabelMixin, StatusMixin, CommentMixin, GenericAttributeMixin, BaseModel @@ -1298,27 +747,6 @@ class InvestigativeQuestion( "SearchHistory", backref="investigativequestion", lazy="select" ) - def __init__( - self, name, display_name, dfiq_identifier, user, spec_json, description=None - ): - """Initialize the InvestigativeQuestion object. - - Args: - name (str): The name of the question - display_name (str): The display name of the question - dfiq_identifier (str): DFIQ identifier for question - user (timesketch.models.user.User): A user - spec_json (str): Question specification - description (str): Description of the question - """ - super().__init__() - self.name = name - self.display_name = display_name - self.dfiq_identifier = dfiq_identifier - self.user = user - self.spec_json = spec_json - self.description = description - # Association tables for the many-to-many relationships for an approach. approach_searchtemplate_association_table = Table( @@ -1368,24 +796,3 @@ class InvestigativeQuestionApproach( search_histories = relationship( "SearchHistory", backref="investigativequestionapproach", lazy="select" ) - - def __init__( - self, name, display_name, dfiq_identifier, user, spec_json, description=None - ): - """Initialize the InvestigativeQuestion object. - - Args: - name (str): The name of the question - display_name (str): The display name of the question - dfiq_identifier (str): DFIQ identifier for approach - user (timesketch.models.user.User): A user - spec_json (str): Question specification - description (str): Description of the question - """ - super().__init__() - self.name = name - self.display_name = display_name - self.dfiq_identifier = dfiq_identifier - self.user = user - self.spec_json = spec_json - self.description = description diff --git a/timesketch/models/user.py b/timesketch/models/user.py index 45180351dc..adb338b798 100644 --- a/timesketch/models/user.py +++ b/timesketch/models/user.py @@ -56,20 +56,34 @@ class User(UserMixin, BaseModel): email = Column(Unicode(255)) active = Column(Boolean(), default=True) admin = Column(Boolean(), default=False) + # Relationships sketches = relationship("Sketch", backref="user", lazy="dynamic") + analyses = relationship("Analysis", backref="user", lazy="dynamic") + analysissessions = relationship("AnalysisSession", backref="user", lazy="dynamic") searchindices = relationship("SearchIndex", backref="user", lazy="dynamic") timelines = relationship("Timeline", backref="user", lazy="dynamic") views = relationship("View", backref="user", lazy="dynamic") searchhistories = relationship("SearchHistory", backref="user", lazy="dynamic") + searchtemplates = relationship("SearchTemplate", backref="user", lazy="dynamic") stories = relationship("Story", backref="user", lazy="dynamic") aggregations = relationship("Aggregation", backref="user", lazy="dynamic") - datasources = relationship("DataSource", backref="user", lazy="dynamic") aggregationgroups = relationship("AggregationGroup", backref="user", lazy="dynamic") + datasources = relationship("DataSource", backref="user", lazy="dynamic") my_groups = relationship("Group", backref="user", lazy="dynamic") sigmarules = relationship("SigmaRule", backref="user", lazy="dynamic") + attributes = relationship("Attribute", backref="user", lazy="dynamic") + attributevalues = relationship("AttributeValue", backref="user", lazy="dynamic") + graphs = relationship("Graph", backref="user", lazy="dynamic") + scenarios = relationship("Scenario", backref="user", lazy="dynamic") + facets = relationship("Facet", backref="user", lazy="dynamic") + facettimeframes = relationship("FacetTimeFrame", backref="user", lazy="dynamic") + facetconclusions = relationship("FacetConclusion", backref="user", lazy="dynamic") investigative_questions = relationship( "InvestigativeQuestion", backref="user", lazy="dynamic" ) + investigative_question_approaches = relationship( + "InvestigativeQuestionApproach", backref="user", lazy="dynamic" + ) investigative_question_conclusions = relationship( "InvestigativeQuestionConclusion", backref="user", lazy="dynamic" ) @@ -78,19 +92,6 @@ class User(UserMixin, BaseModel): "Group", secondary=user_group, backref=backref("users", lazy="dynamic") ) - def __init__(self, username, name=None): - """Initialize the User object. - - Args: - username: Username for the user - name: Name of the user - """ - super().__init__() - self.username = username - self.name = name - if not name: - self.name = username - def set_password(self, plaintext, rounds=12): """Sets the password for the user. The password hash is created with the Bcrypt python library (http://www.mindrot.org/projects/py-bcrypt/). @@ -124,18 +125,3 @@ class Group(LabelMixin, StatusMixin, BaseModel): display_name = Column(Unicode(255)) description = Column(UnicodeText()) user_id = Column(Integer, ForeignKey("user.id")) - - def __init__(self, name, display_name=None, description=None, user=None): - """Initialize the Group object. - - Args: - name: Name of the group - display_name: User friendly name of the group - description: Description of the group - user: Creator (instance of timesketch.models.user.User) - """ - super().__init__() - self.name = name - self.display_name = display_name or name - self.description = description or name - self.user = user diff --git a/timesketch/tsctl.py b/timesketch/tsctl.py index 364588c28d..f19dca1004 100644 --- a/timesketch/tsctl.py +++ b/timesketch/tsctl.py @@ -78,11 +78,11 @@ def get_password_from_prompt(): if not password: password = get_password_from_prompt() - user = User.get_or_create(username=username) + user = User.get_or_create(username=username, name=username) user.set_password(plaintext=password) db_session.add(user) db_session.commit() - print(f"User {username, password} created/updated") + print(f"User account for {username} created/updated") @cli.command(name="enable-user") @@ -202,7 +202,7 @@ def list_groups(): @click.argument("group_name") def create_group(group_name): """Create a group.""" - group = Group.get_or_create(name=group_name) + group = Group.get_or_create(name=group_name, display_name=group_name) db_session.add(group) db_session.commit() print(f"Group created: {group_name}") @@ -746,7 +746,7 @@ def analyzer_stats(analyzer_name, timeline_id, scope, result_text_search, limit) """Prints analyzer stats.""" if timeline_id: - timeline = Timeline.query.get(timeline_id) + timeline = Timeline.get_by_id(timeline_id) if not timeline: print("No timeline found with this ID.") return diff --git a/timesketch/views/auth.py b/timesketch/views/auth.py index 04cd78e076..5a71a497e7 100644 --- a/timesketch/views/auth.py +++ b/timesketch/views/auth.py @@ -139,7 +139,7 @@ def login(): group_name = group_name.lstrip(not_member_sign) # Get or create the group in the Timesketch database. - group = Group.get_or_create(name=group_name) + group = Group.get_or_create(name=group_name, display_name=group_name) if remove_group: if group in user.groups: @@ -148,6 +148,8 @@ def login(): if group not in user.groups: user.groups.append(group) # Commit the changes to the database. + db_session.add(user) + db_session.add(group) db_session.commit() # Login form POST