Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impossible to create many timelines from 1 OS index #3166

Open
mcichorzA opened this issue Aug 23, 2024 · 1 comment
Open

Impossible to create many timelines from 1 OS index #3166

mcichorzA opened this issue Aug 23, 2024 · 1 comment
Labels

Comments

@mcichorzA
Copy link

Describe the bug
Timesketch API doesn't allow to create many timelines from 1 ES index and also has bug in TimelineListResource.
Currently I am not able to create 2 timelines using 1 OpenSearch index (not counting web upload of csv/json/plaso which supports that).
I want to have 1 index per sketch. I upload data directly to OpenSearch with psort (data is already with timeline id set properly). Currently I have found impossible to create 2 Timelines from such 1 index.

Things I have tried:

  1. generate_timeline_from_es_index
    # Step 1: Make sure the index doesn't exist already.
    for index_obj in self.api.list_searchindices():
    if index_obj is None:
    continue
    if index_obj.index_name == es_index_name:
    raise ValueError("Unable to add the ES index, since it already exists.")

    This doesnt allow passing es_index_name of index which already is assigned to other SearchIndex (index_obj.index_name is the actual name of opensearch index, not the same thing as index_name passed to this funciton.
  2. Using API directly
    def post(self, sketch_id):
    """Handles POST request to the resource.
    Returns:
    A sketch in JSON (instance of flask.wrappers.Response)
    """
    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"):
    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.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.get_by_id(timeline_id)

    This won't work because
timeline_id = [
            t.searchindex.id
            for t in sketch.timelines
            if t.searchindex.id == searchindex_id
        ]

this will save to variable timeline_id ID of already existing searchindex (cool), but then it wont enter IF when Timeline is created

else:
metadata["created"] = False
return_code = HTTP_STATUS_CODE_OK
timeline = Timeline.get_by_id(timeline_id)

Whats strange it will return TIMELINE object based on the SEARCHINDEX ID which is a BUG, because if you look at tables of Searchindex and TImelines theirs IDs dont match up.

To Reproduce
Steps to reproduce the behavior:

  1. Create dummy index in OS.
  2. Create first timeline from this index (eg with generate_timeline_from_es_index)
  3. Try to create another timeline with the same index <- Impossible with API

Expected behavior
Similar to the situation when 2 csv/json files are uploaded to the same index, it should be also possible from API

Screenshots
N/A

Desktop (please complete the following information):
N/A

Additional context
I can probide if asked

@mcichorzA mcichorzA added the Bug label Aug 23, 2024
@mcichorzA
Copy link
Author

Perhaps this could workout, but I dont see it in routes.

class TimelineCreateResource(resources.ResourceMixin, Resource):
"""Resource to create a timeline."""
@login_required
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.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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant