def home(): """Generates the home page view template. Returns: Template with context. """ form = HiddenNameDescriptionForm() sketches = Sketch.all_with_acl().filter( not_(Sketch.Status.status == u'deleted'), Sketch.Status.parent).order_by(Sketch.updated_at.desc()) # Only render upload button if it is configured. upload_enabled = current_app.config[u'UPLOAD_ENABLED'] # Handle form for creating a new sketch. if form.validate_on_submit(): sketch = Sketch(name=form.name.data, description=form.description.data, user=current_user) sketch.status.append(sketch.Status(user=None, status=u'new')) # Give the requesting user permissions on the new sketch. sketch.grant_permission(permission=u'read', user=current_user) sketch.grant_permission(permission=u'write', user=current_user) sketch.grant_permission(permission=u'delete', user=current_user) db_session.add(sketch) db_session.commit() return redirect(url_for(u'sketch_views.overview', sketch_id=sketch.id)) return render_template(u'home/home.html', sketches=sketches, form=form, upload_enabled=upload_enabled)
def _create_mock_event(event_id, quantity, time_diffs=None, source_attrs=None): """ Returns an instance of Event, based on the MockDataStore event_dict example. Args: event_id: Desired ID for the Event. quantity: The number of Events to be generated. time_diffs: A list of time differences between the generated Events. source_attrs: Dictionary of attributes to add to the source of the generated events. Returns: A generator of Event objects. """ if not time_diffs: time_diffs = [0] if quantity < 0: quantity = abs(quantity) # If the list of time differences is too small to be compatible # with the quantity of events, then extend the list with the last # value for as many items as necessary. if quantity - len(time_diffs) > 0: time_diffs.extend([time_diffs[len(time_diffs) - 1]] * (quantity - len(time_diffs))) # Setup for Event object initialisation ds = MockDataStore('test', 0) user = User('test_user') sketch = Sketch('test_sketch', 'description', user) label = sketch.Label(label='Test label', user=user) sketch.labels.append(label) event_timestamp = 1410895419859714 event_template = ds.get_event('test', 'test') for i in range(quantity): eventObj = _create_eventObj(ds, sketch, event_template, event_id, event_timestamp, source_attrs) yield eventObj # adding extra events after every requested event for better # simulation of real timeline data i.e. working with a larger # dataset for _ in range(100): event_timestamp += 1 event_id += 1 eventObj = _create_eventObj(ds, sketch, event_template, event_id, event_timestamp, source_attrs) yield eventObj event_timestamp += abs(time_diffs[i]) event_id += 1
def home(): """Generates the home page view template. Returns: Template with context. """ form = HiddenNameDescriptionForm() sketches = Sketch.all_with_acl().filter( not_(Sketch.Status.status == 'deleted'), Sketch.Status.parent).order_by(Sketch.updated_at.desc()) # Only render upload button if it is configured. upload_enabled = current_app.config['UPLOAD_ENABLED'] # Handle form for creating a new sketch. if form.validate_on_submit(): sketch = Sketch( name=form.name.data, description=form.description.data, user=current_user) sketch.status.append(sketch.Status(user=None, status='new')) db_session.add(sketch) db_session.commit() # Give the requesting user permissions on the new sketch. sketch.grant_permission(permission='read', user=current_user) sketch.grant_permission(permission='write', user=current_user) sketch.grant_permission(permission='delete', user=current_user) return redirect(url_for('sketch_views.overview', sketch_id=sketch.id)) return render_template( 'home/home.html', sketches=sketches, form=form, upload_enabled=upload_enabled)
def home(): """Generates the home page view template. Returns: Template with context. """ form = HiddenNameDescriptionForm() sketches = Sketch.all_with_acl().filter( not_(Sketch.Status.status == u'deleted'), Sketch.Status.parent).order_by(Sketch.updated_at.desc()) query_filter = request.args.get(u'filter', u'') query = request.args.get(u'q', u'') # Only render upload button if it is configured. upload_enabled = current_app.config[u'UPLOAD_ENABLED'] last_sketch = View.query.filter_by(user=current_user, name=u'').order_by( View.updated_at.desc()).first() if query_filter: if query_filter == u'user': sketches = sketches.filter(Sketch.user == current_user) elif query_filter == u'shared': sketches = sketches.filter(not_(Sketch.user == current_user)) # TODO: Figure out a better way to handle this. if query: if query.startswith(u'*'): query = u'' else: sketches = sketches.filter(Sketch.name.contains(query)).limit(100) # Handle form for creating a new sketch. if form.validate_on_submit(): sketch = Sketch(name=form.name.data, description=form.description.data, user=current_user) sketch.status.append(sketch.Status(user=None, status=u'new')) # Give the requesting user permissions on the new sketch. sketch.grant_permission(permission=u'read', user=current_user) sketch.grant_permission(permission=u'write', user=current_user) sketch.grant_permission(permission=u'delete', user=current_user) db_session.add(sketch) db_session.commit() return redirect(url_for(u'sketch_views.overview', sketch_id=sketch.id)) return render_template(u'home/home.html', sketches=sketches, form=form, query=query, upload_enabled=upload_enabled, last_sketch=last_sketch)
def get(self): """Handles GET request to the resource. Returns: List of sketches (instance of flask.wrappers.Response) """ if current_user.admin: sketch_query = Sketch.query else: sketch_query = Sketch.all_with_acl() filtered_sketches = sketch_query.filter( not_(Sketch.Status.status == 'deleted'), Sketch.Status.parent).order_by(Sketch.updated_at.desc()).all() # Just return a subset of the sketch objects to reduce the amount of # data returned. sketches = [] for sketch in filtered_sketches: sketches.append({ 'name': sketch.name, 'updated_at': str(sketch.updated_at), 'user': sketch.user.username, 'id': sketch.id, 'status': sketch.get_status.status }) meta = {'current_user': current_user.username} return jsonify({'objects': sketches, 'meta': meta})
def _create_sketch(self, name, user, acl=False): """Create a sketch in the database. Args: name: Name of the sketch (string) user: A user (instance of timesketch.models.user.User) acl: Boolean value to decide if ACL permissions should be set Returns: A sketch (instance of timesketch.models.sketch.Sketch) """ sketch = Sketch(name=name, description=name, user=user) if acl: for permission in [u'read', u'write', u'delete']: sketch.grant_permission(user=user, permission=permission) label = sketch.Label(label=u'Test label', user=user) status = sketch.Status(status=u'Test status', user=user) sketch.labels.append(label) sketch.status.append(status) self._commit_to_database(sketch) return sketch
def test_get_event_data(self): """Test getEventData returns the correct values.""" user = User("test_user") sketch = Sketch("test_sketch", "description", user) label = sketch.Label(label="Test label", user=user) sketch.labels.append(label) index = "test_index" sketch_id = 1 for analyzer_class in self.analyzer_classes: analyzer = analyzer_class["class"](index, sketch_id) datastore = analyzer.datastore event_dict = copy.deepcopy(MockDataStore.event_dict) event_dict["_source"].update({"xml_string": xml_string1}) event_obj = Event(event_dict, datastore, sketch) username = analyzer.getEventData(event_obj, "TargetUserName") logon_id = analyzer.getEventData(event_obj, "TargetLogonId") self.assertEqual(username, "USER_1") self.assertEqual(logon_id, "0x0000000000000001")
def test_get_event_data(self): """Test getEventData returns the correct values.""" user = User('test_user') sketch = Sketch('test_sketch', 'description', user) label = sketch.Label(label='Test label', user=user) sketch.labels.append(label) index = 'test_index' sketch_id = 1 for analyzer_class in self.analyzer_classes: analyzer = analyzer_class['class'](index, sketch_id) datastore = analyzer.datastore event_dict = copy.deepcopy(MockDataStore.event_dict) event_dict['_source'].update({'xml_string': xml_string1}) event_obj = Event(event_dict, datastore, sketch) username = analyzer.getEventData(event_obj, 'TargetUserName') logon_id = analyzer.getEventData(event_obj, 'TargetLogonId') self.assertEqual(username, 'USER_1') self.assertEqual(logon_id, '0x0000000000000001')
def home(): """Generates the home page view template. Returns: Template with context. """ form = HiddenNameDescriptionForm() sketches = Sketch.all_with_acl().filter( not_(Sketch.Status.status == u'deleted'), Sketch.Status.parent).order_by(Sketch.updated_at.desc()) query_filter = request.args.get(u'filter', u'') query = request.args.get(u'q', u'') # Only render upload button if it is configured. upload_enabled = current_app.config[u'UPLOAD_ENABLED'] last_sketch = View.query.filter_by( user=current_user, name=u'').order_by( View.updated_at.desc()).first() if query_filter: if query_filter == u'user': sketches = sketches.filter(Sketch.user == current_user) elif query_filter == u'shared': sketches = sketches.filter(not_(Sketch.user == current_user)) # TODO: Figure out a better way to handle this. if query: if query.startswith(u'*'): query = u'' else: sketches = sketches.filter(Sketch.name.contains(query)).limit(100) # Handle form for creating a new sketch. if form.validate_on_submit(): sketch = Sketch( name=form.name.data, description=form.description.data, user=current_user) sketch.status.append(sketch.Status(user=None, status=u'new')) # Give the requesting user permissions on the new sketch. sketch.grant_permission(current_user, u'read') sketch.grant_permission(current_user, u'write') sketch.grant_permission(current_user, u'delete') db_session.add(sketch) db_session.commit() return redirect(url_for(u'sketch_views.overview', sketch_id=sketch.id)) return render_template( u'home/home.html', sketches=sketches, form=form, query=query, upload_enabled=upload_enabled, last_sketch=last_sketch)
def post(self): """Handles POST request to the resource. Returns: A sketch in JSON (instance of flask.wrappers.Response) """ form = NameDescriptionForm.build(request) if form.validate_on_submit(): sketch = Sketch(name=form.name.data, description=form.description.data, user=current_user) sketch.status.append(sketch.Status(user=None, status=u'new')) # Give the requesting user permissions on the new sketch. sketch.grant_permission(permission=u'read', user=current_user) sketch.grant_permission(permission=u'write', user=current_user) sketch.grant_permission(permission=u'delete', user=current_user) db_session.add(sketch) db_session.commit() return self.to_json(sketch, status_code=HTTP_STATUS_CODE_CREATED) return abort(HTTP_STATUS_CODE_BAD_REQUEST)
def _create_sketch(self, name, user, acl=False): """Create a sketch in the database. Args: name: Name of the sketch (string) user: A user (instance of timesketch.models.user.User) acl: Boolean value to decide if ACL permissions should be set Returns: A sketch (instance of timesketch.models.sketch.Sketch) """ sketch = Sketch.get_or_create(name=name, description=name, user=user) if acl: for permission in ['read', 'write', 'delete']: sketch.grant_permission(permission=permission, user=user) label = sketch.Label(label='Test label', user=user) status = sketch.Status(status='Test status', user=user) sketch.labels.append(label) sketch.status.append(status) self._commit_to_database(sketch) return sketch
def get(self): """Handles GET request to the resource. Returns: List of sketches (instance of flask.wrappers.Response) """ # TODO: Handle offset parameter sketches = Sketch.all_with_acl() paginated_result = sketches.paginate(1, 10, False) meta = { u'next': paginated_result.next_num, u'previous': paginated_result.prev_num, u'offset': paginated_result.page, u'limit': paginated_result.per_page } if not paginated_result.has_prev: meta[u'previous'] = None if not paginated_result.has_next: meta[u'next'] = None result = self.to_json(paginated_result.items, meta=meta) return result
def post(self): """Handles POST request to the resource. Returns: A sketch in JSON (instance of flask.wrappers.Response) """ form = NameDescriptionForm.build(request) if form.validate_on_submit(): sketch = Sketch( name=form.name.data, description=form.description.data, user=current_user) sketch.status.append(sketch.Status(user=None, status=u'new')) # Give the requesting user permissions on the new sketch. sketch.grant_permission(permission=u'read', user=current_user) sketch.grant_permission(permission=u'write', user=current_user) sketch.grant_permission(permission=u'delete', user=current_user) db_session.add(sketch) db_session.commit() return self.to_json(sketch, status_code=HTTP_STATUS_CODE_CREATED) return abort(HTTP_STATUS_CODE_BAD_REQUEST)
def get(self): """Handles GET request to the resource. Returns: List of sketches (instance of flask.wrappers.Response) """ args = self.parser.parse_args() scope = args.get("scope") page = args.get("page") per_page = args.get("per_page") search_query = args.get("search_query") include_archived = args.get("include_archived") if current_user.admin and scope == "admin": sketch_query = Sketch.query else: sketch_query = Sketch.all_with_acl() base_filter = sketch_query.filter( not_(Sketch.Status.status == "deleted"), not_(Sketch.Status.status == "archived"), Sketch.Status.parent, ).order_by(Sketch.updated_at.desc()) base_filter_with_archived = sketch_query.filter( not_(Sketch.Status.status == "deleted"), Sketch.Status.parent).order_by(Sketch.updated_at.desc()) filtered_sketches = base_filter_with_archived sketches = [] return_sketches = [] has_next = False has_prev = False next_page = None prev_page = None current_page = 1 total_pages = 0 total_items = 0 if scope == "recent": # Get list of sketches that the user has actively searched in. # TODO: Make this cover more actions such as story updates etc. # TODO: Right now we only return the top 3, make this configurable. views = (View.query.filter_by(user=current_user, name="").order_by( View.updated_at.desc()).limit(3)) sketches = [view.sketch for view in views] total_items = len(sketches) elif scope == "admin": if not current_user.admin: abort(HTTP_STATUS_CODE_FORBIDDEN, "User is not an admin.") if include_archived: filtered_sketches = base_filter_with_archived else: filtered_sketches = base_filter elif scope == "user": filtered_sketches = base_filter.filter_by(user=current_user) elif scope == "archived": filtered_sketches = sketch_query.filter( Sketch.status.any(status="archived")) elif scope == "shared": filtered_sketches = base_filter.filter(Sketch.user != current_user) elif scope == "search": filtered_sketches = base_filter_with_archived.filter( or_( Sketch.name.ilike(f"%{search_query}%"), Sketch.description.ilike(f"%{search_query}%"), )) if not sketches: pagination = filtered_sketches.paginate(page=page, per_page=per_page) sketches = pagination.items has_next = pagination.has_next has_prev = pagination.has_prev next_page = pagination.next_num prev_page = pagination.prev_num current_page = pagination.page total_pages = pagination.pages total_items = pagination.total for sketch in sketches: # Return a subset of the sketch objects to reduce the amount of # data sent to the client. return_sketches.append({ "id": sketch.id, "name": sketch.name, "description": sketch.description, "created_at": str(sketch.created_at), "last_activity": utils.get_sketch_last_activity(sketch), "user": sketch.user.username, "status": sketch.get_status.status, }) meta = { "current_user": current_user.username, "has_next": has_next, "has_prev": has_prev, "next_page": next_page, "prev_page": prev_page, "current_page": current_page, "total_pages": total_pages, "total_items": total_items, } return jsonify({"objects": return_sketches, "meta": meta})
def run(self, file_path, sketch_id, username, timeline_name): """This is the run method.""" file_path = os.path.realpath(file_path) file_path_no_extension, extension = os.path.splitext(file_path) extension = extension.lstrip('.') filename = os.path.basename(file_path_no_extension) supported_extensions = ('plaso', 'csv', 'jsonl') if not os.path.isfile(file_path): sys.exit('No such file: {0:s}'.format(file_path)) if extension not in supported_extensions: sys.exit('Extension {0:s} is not supported. ' '(supported extensions are: {1:s})'.format( extension, ', '.join(supported_extensions))) user = None if not username: username = pwd.getpwuid(os.stat(file_path).st_uid).pw_name if not username == 'root': if not isinstance(username, six.text_type): username = codecs.decode(username, 'utf-8') user = User.query.filter_by(username=username).first() if not user: sys.exit('Cannot determine user for file: {0:s}'.format(file_path)) sketch = None # If filename starts with <number> then use that as sketch_id. # E.g: 42_file_name.plaso means sketch_id is 42. sketch_id_from_filename = filename.split('_')[0] if not sketch_id and sketch_id_from_filename.isdigit(): sketch_id = sketch_id_from_filename if sketch_id: try: sketch = Sketch.query.get_with_acl(sketch_id, user=user) except Forbidden: pass if not timeline_name: if timeline_name is None: timeline_name = '{0:s}_timeline'.format(filename) if not isinstance(timeline_name, six.text_type): timeline_name = codecs.decode(timeline_name, 'utf-8') timeline_name = timeline_name.replace('_', ' ') # Remove sketch ID if present in the filename. timeline_parts = timeline_name.split() if timeline_parts[0].isdigit(): timeline_name = ' '.join(timeline_name.split()[1:]) if not sketch: # Create a new sketch. sketch_name = 'Sketch for: {0:s}'.format(timeline_name) sketch = Sketch(name=sketch_name, description=sketch_name, user=user) # Need to commit here to be able to set permissions later. db_session.add(sketch) db_session.commit() sketch.grant_permission(permission='read', user=user) sketch.grant_permission(permission='write', user=user) sketch.grant_permission(permission='delete', user=user) sketch.status.append(sketch.Status(user=None, status='new')) db_session.add(sketch) db_session.commit() index_name = uuid.uuid4().hex if not isinstance(index_name, six.text_type): index_name = codecs.decode(index_name, 'utf-8') searchindex = SearchIndex.get_or_create(name=timeline_name, description=timeline_name, user=user, index_name=index_name) searchindex.grant_permission(permission='read', user=user) searchindex.grant_permission(permission='write', user=user) searchindex.grant_permission(permission='delete', user=user) searchindex.set_status('processing') db_session.add(searchindex) db_session.commit() if sketch and sketch.has_permission(user, 'write'): timeline = Timeline(name=searchindex.name, description=searchindex.description, sketch=sketch, user=user, searchindex=searchindex) timeline.set_status('processing') sketch.timelines.append(timeline) db_session.add(timeline) db_session.commit() # Start Celery pipeline for indexing and analysis. # Import here to avoid circular imports. from timesketch.lib import tasks # pylint: disable=import-outside-toplevel pipeline = tasks.build_index_pipeline(file_path=file_path, events='', timeline_name=timeline_name, index_name=index_name, file_extension=extension, sketch_id=sketch.id) pipeline.apply_async(task_id=index_name) print('Imported {0:s} to sketch: {1:d} ({2:s})'.format( file_path, sketch.id, sketch.name))
def setup_sketch(timeline_name, index_name, username, sketch_id=None): """Use existing sketch or create a new sketch. Args: timeline_name: (str) Name of the Timeline index_name: (str) Name of the index username: (str) Who should own the timeline sketch_id: (str) Optional sketch_id to add timeline to Returns: (tuple) sketch ID and timeline ID as integers """ with app.app_context(): user = User.get_or_create(username=username) sketch = None if sketch_id: try: sketch = Sketch.query.get_with_acl(sketch_id, user=user) logger.info('Using existing sketch: {} ({})'.format( sketch.name, sketch.id)) except Forbidden: pass if not (sketch or sketch_id): # Create a new sketch. sketch_name = 'Turbinia: {}'.format(timeline_name) sketch = Sketch(name=sketch_name, description=sketch_name, user=user) # Need to commit here to be able to set permissions later. db_session.add(sketch) db_session.commit() sketch.grant_permission(permission='read', user=user) sketch.grant_permission(permission='write', user=user) sketch.grant_permission(permission='delete', user=user) sketch.status.append(sketch.Status(user=None, status='new')) db_session.add(sketch) db_session.commit() logger.info('Created new sketch: {} ({})'.format( sketch.name, sketch.id)) searchindex = SearchIndex.get_or_create( name=timeline_name, description='Created by Turbinia.', user=user, index_name=index_name) searchindex.grant_permission(permission='read', user=user) searchindex.grant_permission(permission='write', user=user) searchindex.grant_permission(permission='delete', user=user) searchindex.set_status('processing') db_session.add(searchindex) db_session.commit() timeline = Timeline(name=searchindex.name, description=searchindex.description, sketch=sketch, user=user, searchindex=searchindex) # If the user doesn't have write access to the sketch then create the # timeline but don't attach it to the sketch. if not sketch.has_permission(user, 'write'): timeline.sketch = None else: sketch.timelines.append(timeline) db_session.add(timeline) db_session.commit() timeline.set_status('processing') return sketch.id, timeline.id
def get(self): """Handles GET request to the resource. Returns: List of sketches (instance of flask.wrappers.Response) """ args = self.parser.parse_args() scope = args.get('scope') page = args.get('page') per_page = args.get('per_page') search_query = args.get('search_query') include_archived = args.get('include_archived') if current_user.admin and scope == 'admin': sketch_query = Sketch.query else: sketch_query = Sketch.all_with_acl() base_filter = sketch_query.filter( not_(Sketch.Status.status == 'deleted'), not_(Sketch.Status.status == 'archived'), Sketch.Status.parent).order_by(Sketch.updated_at.desc()) base_filter_with_archived = sketch_query.filter( not_(Sketch.Status.status == 'deleted'), Sketch.Status.parent).order_by(Sketch.updated_at.desc()) filtered_sketches = base_filter_with_archived sketches = [] return_sketches = [] has_next = False has_prev = False next_page = None prev_page = None current_page = 1 total_pages = 0 total_items = 0 if scope == 'recent': # Get list of sketches that the user has actively searched in. # TODO: Make this cover more actions such as story updates etc. # TODO: Right now we only return the top 3, make this configurable. views = View.query.filter_by( user=current_user, name='').order_by( View.updated_at.desc()).limit(3) sketches = [view.sketch for view in views] total_items = len(sketches) elif scope == 'admin': if not current_user.admin: abort(HTTP_STATUS_CODE_FORBIDDEN, 'User is not an admin.') if include_archived: filtered_sketches = base_filter_with_archived else: filtered_sketches = base_filter elif scope == 'user': filtered_sketches = base_filter.filter_by(user=current_user) elif scope == 'archived': filtered_sketches = sketch_query.filter( Sketch.status.any(status='archived')) elif scope == 'shared': filtered_sketches = base_filter.filter(Sketch.user != current_user) elif scope == 'search': filtered_sketches = base_filter_with_archived.filter( or_( Sketch.name.ilike(f'%{search_query}%'), Sketch.description.ilike(f'%{search_query}%') ) ) if not sketches: pagination = filtered_sketches.paginate( page=page, per_page=per_page) sketches = pagination.items has_next = pagination.has_next has_prev = pagination.has_prev next_page = pagination.next_num prev_page = pagination.prev_num current_page = pagination.page total_pages = pagination.pages total_items = pagination.total for sketch in sketches: # Return a subset of the sketch objects to reduce the amount of # data sent to the client. return_sketches.append({ 'id': sketch.id, 'name': sketch.name, 'description': sketch.description, 'created_at': str(sketch.created_at), 'last_activity': utils.get_sketch_last_activity(sketch), 'user': sketch.user.username, 'status': sketch.get_status.status }) meta = { 'current_user': current_user.username, 'has_next': has_next, 'has_prev': has_prev, 'next_page': next_page, 'prev_page': prev_page, 'current_page': current_page, 'total_pages': total_pages, 'total_items': total_items } return jsonify({'objects': return_sketches, 'meta': meta})
def run(self, file_path, sketch_id, username, timeline_name): """This is the run method.""" file_path = os.path.realpath(file_path) file_path_no_extension, extension = os.path.splitext(file_path) extension = extension.lstrip('.') filename = os.path.basename(file_path_no_extension) supported_extensions = ('plaso', 'csv', 'jsonl') if not os.path.isfile(file_path): sys.exit('No such file: {0:s}'.format(file_path)) if extension not in supported_extensions: sys.exit( 'Extension {0:s} is not supported. ' '(supported extensions are: {1:s})'.format( extension, ', '.join(supported_extensions))) user = None if not username: username = pwd.getpwuid(os.stat(file_path).st_uid).pw_name if not username == 'root': if not isinstance(username, six.text_type): username = codecs.decode(username, 'utf-8') user = User.query.filter_by(username=username).first() if not user: sys.exit('Cannot determine user for file: {0:s}'.format(file_path)) sketch = None # If filename starts with <number> then use that as sketch_id. # E.g: 42_file_name.plaso means sketch_id is 42. sketch_id_from_filename = filename.split('_')[0] if not sketch_id and sketch_id_from_filename.isdigit(): sketch_id = sketch_id_from_filename if sketch_id: try: sketch = Sketch.query.get_with_acl(sketch_id, user=user) except Forbidden: pass if not timeline_name: if not isinstance(timeline_name, six.text_type): timeline_name = codecs.decode(timeline_name, 'utf-8') timeline_name = timeline_name.replace('_', ' ') # Remove sketch ID if present in the filename. timeline_parts = timeline_name.split() if timeline_parts[0].isdigit(): timeline_name = ' '.join(timeline_name.split()[1:]) if not sketch: # Create a new sketch. sketch_name = 'Sketch for: {0:s}'.format(timeline_name) sketch = Sketch( name=sketch_name, description=sketch_name, user=user) # Need to commit here to be able to set permissions later. db_session.add(sketch) db_session.commit() sketch.grant_permission(permission='read', user=user) sketch.grant_permission(permission='write', user=user) sketch.grant_permission(permission='delete', user=user) sketch.status.append(sketch.Status(user=None, status='new')) db_session.add(sketch) db_session.commit() index_name = uuid.uuid4().hex if not isinstance(index_name, six.text_type): index_name = codecs.decode(index_name, 'utf-8') searchindex = SearchIndex.get_or_create( name=timeline_name, description=timeline_name, user=user, index_name=index_name) searchindex.grant_permission(permission='read', user=user) searchindex.grant_permission(permission='write', user=user) searchindex.grant_permission(permission='delete', user=user) searchindex.set_status('processing') db_session.add(searchindex) db_session.commit() if sketch and sketch.has_permission(user, 'write'): timeline = Timeline( name=searchindex.name, description=searchindex.description, sketch=sketch, user=user, searchindex=searchindex) timeline.set_status('processing') sketch.timelines.append(timeline) db_session.add(timeline) db_session.commit() # Start Celery pipeline for indexing and analysis. # Import here to avoid circular imports. from timesketch.lib import tasks pipeline = tasks.build_index_pipeline( file_path, timeline_name, index_name, extension, sketch.id) pipeline.apply_async(task_id=index_name) print('Imported {0:s} to sketch: {1:d} ({2:s})'.format( file_path, sketch.id, sketch.name))
def get(self): """Handles GET request to the resource. Returns: List of sketches (instance of flask.wrappers.Response) """ args = self.parser.parse_args() scope = args.get('scope', None) page = args.get('page', 1) search_query = args.get('search_query', None) if current_user.admin: sketch_query = Sketch.query else: sketch_query = Sketch.all_with_acl() base_filter = sketch_query.filter( not_(Sketch.Status.status == 'deleted'), not_(Sketch.Status.status == 'archived'), Sketch.Status.parent).order_by(Sketch.updated_at.desc()) base_filter_with_archived = sketch_query.filter( not_(Sketch.Status.status == 'deleted'), Sketch.Status.parent).order_by(Sketch.updated_at.desc()) filtered_sketches = base_filter_with_archived sketches = [] return_sketches = [] num_hits = 0 if scope == 'recent': # Get list of sketches that the user has actively searched in. # TODO: Make this cover more actions such as story updates etc. # TODO: Right now we only return the top 3, make this configurable. views = View.query.filter_by(user=current_user, name='').order_by( View.updated_at.desc()).limit(3) sketches = [view.sketch for view in views] num_hits = len(sketches) elif scope == 'user': filtered_sketches = base_filter.filter_by(user=current_user) elif scope == 'archived': filtered_sketches = sketch_query.filter( Sketch.status.any(status='archived')) elif scope == 'shared': filtered_sketches = base_filter.filter(Sketch.user != current_user) elif scope == 'search': filtered_sketches = base_filter_with_archived.filter( or_(Sketch.name.ilike(f'%{search_query}%'), Sketch.description.ilike(f'%{search_query}%'))) # If no scope is set, fall back to returning all sketches. if not scope: sketches = filtered_sketches.all() if not sketches: pagination = filtered_sketches.paginate(page=page, per_page=20) sketches = pagination.items num_hits = pagination.total for sketch in sketches: # Last time a user did a query in the sketch, indicating activity. try: last_activity = View.query.filter_by( sketch=sketch, name='').order_by( View.updated_at.desc()).first().updated_at except AttributeError: last_activity = '' # Return a subset of the sketch objects to reduce the amount of # data sent to the client. return_sketches.append({ 'id': sketch.id, 'name': sketch.name, 'description': sketch.description, 'created_at': str(sketch.created_at), 'last_activity': str(last_activity), 'user': sketch.user.username, 'status': sketch.get_status.status }) meta = {'current_user': current_user.username, 'num_hits': num_hits} return jsonify({'objects': return_sketches, 'meta': meta})