def rpc_database_view_rows(handler, session, table_name, page=0, query_filter=None): """ Retrieve the rows from the specified table where the search criteria matches. :param str table_name: The name of the database table to query. :param int page: The page number to retrieve results for. :param dict query_filter: A dictionary mapping optional search criteria for matching the query. :return: A dictionary with columns and rows keys. :rtype: dict """ metatable = database_tables.get(table_name) if not metatable: raise errors.KingPhisherAPIError("failed to get table object for: {0}".format(table_name)) query_filter = query_filter or {} for column in query_filter.keys(): if column not in metatable.column_names: raise errors.KingPhisherAPIError("column {0} is invalid for table {1}".format(column, table_name)) offset = page * VIEW_ROW_COUNT # it's critical that the columns are in the order that the client is expecting rows = [] query = session.query(metatable.model) query = query.filter_by(**query_filter) total_rows = query.count() for row in query[offset:]: if len(rows) == VIEW_ROW_COUNT: break if row.session_has_permissions('r', handler.rpc_session): rows.append([getattr(row, c) for c in metatable.column_names]) if not len(rows): return None return {'columns': metatable.column_names, 'rows': rows, 'total_rows': total_rows, 'page_size': VIEW_ROW_COUNT}
def rpc_database_set_row_value(handler, session, table_name, row_id, keys, values): """ Set values for a row in the specified table with an id of *row_id*. :param str table_name: The name of the database table to set the values of the specified row. :param tuple keys: The column names of *values*. :param tuple values: The values to be updated in the row. """ if not isinstance(keys, (list, tuple)): keys = (keys, ) if not isinstance(values, (list, tuple)): values = (values, ) if len(keys) != len(values): raise errors.KingPhisherAPIError( 'the number of keys does not match the number of values') table = database_table_objects.get(table_name) if not table: raise errors.KingPhisherAPIError( "failed to get table object for: {0}".format(table_name)) for key, value in zip(keys, values): if key not in database_tables[table_name]: raise errors.KingPhisherAPIError( "column {0} is invalid for table {1}".format(key, table_name)) row = db_manager.get_row_by_id(session, table, row_id) if not row: raise errors.KingPhisherAPIError( "failed to get row id: {0} from table: {1}".format( row_id, table_name)) row.assert_session_has_permissions('u', handler.rpc_session) for key, value in zip(keys, values): setattr(row, key, value) row.assert_session_has_permissions('u', handler.rpc_session) session.commit()
def rpc_database_insert_row(handler, session, table_name, keys, values): """ Insert a new row into the specified table. :param str table_name: The name of the database table to insert a new row into. :param list keys: The column names of *values*. :param list values: The values to be inserted in the row. :return: The id of the new row that has been added. """ if not isinstance(keys, (list, tuple)): keys = (keys, ) if not isinstance(values, (list, tuple)): values = (values, ) if len(keys) != len(values): raise errors.KingPhisherAPIError( 'the number of keys does not match the number of values') table = database_table_objects.get(table_name) if not table: raise errors.KingPhisherAPIError( "failed to get table object for: {0}".format(table_name)) for key in keys: if key not in database_tables[table_name]: raise errors.KingPhisherAPIError( "column {0} is invalid for table {1}".format(key, table_name)) row = table() for key, value in zip(keys, values): setattr(row, key, value) row.assert_session_has_permissions('c', handler.rpc_session) session.add(row) session.commit() return row.id
def rpc_database_insert_row_multi(handler, session, table_name, keys, rows, deconflict_ids=False): """ Insert multiple new rows into the specified table. If *deconflict_ids* is true, new id values will be assigned as necessary to merge the data into the database. This function will fail if constraints for the table are not met. :param str table_name: The name of the database table to insert data into. :param list keys: The column names of the values in *rows*. :param list rows: A list of rows, each row is a list of values ordered and identified by *keys* to be inserted. :return: List of ids of the newly inserted rows. :rtype: list """ inserted_rows = collections.deque() if not isinstance(keys, list): keys = list(keys) if not isinstance(rows, list): rows = list(rows) table = database_table_objects.get(table_name) if not table: raise errors.KingPhisherAPIError( 'failed to get table object for: {0}'.format(table_name)) for key in keys: if key not in database_tables[table_name]: raise errors.KingPhisherAPIError( 'column {0} is invalid for table {1}'.format(keys, table_name)) for row in rows: if len(row) != len(keys): raise errors.KingPhisherAPIError( 'row is not the same length as the number of values defined') row = dict(zip(keys, row)) if 'id' in row and db_manager.get_row_by_id(session, table, row['id']) is not None: if deconflict_ids: row['id'] = None else: raise errors.KingPhisherAPIError( 'row id conflicts with an existing value') table_row = table(**row) table_row.assert_session_has_permissions('c', handler.rpc_session) session.add(table_row) inserted_rows.append(table_row) session.commit() return [row.id for row in inserted_rows]
def rpc_events_unsubscribe(handler, event_id, event_types=None, attributes=None): """ Unsubscribe from an event published by the server that the client previously subscribed to. :param str event_id: The identifier of the event to subscribe to. :param list event_types: A list of sub-types for the corresponding event. :param list attributes: A list of attributes of the event object to be sent to the client. """ if not isinstance(event_id, str): raise errors.KingPhisherAPIError('a valid event id must be specified') event_socket = handler.rpc_session.event_socket if event_socket is None: raise errors.KingPhisherAPIError('the event socket is not open for this session') return event_socket.unsubscribe(event_id, event_types=event_types, attributes=attributes)
def rpc_database_delete_rows_by_id(handler, session, table_name, row_ids): """ Delete multiple rows from a table with the specified values in the id column. If a row id specified in *row_ids* does not exist, then it will be skipped and no error will be thrown. :param str table_name: The name of the database table to delete rows from. :param list row_ids: The row ids to delete. :return: The row ids that were deleted. :rtype: list """ table = database_table_objects.get(table_name) if not table: raise errors.KingPhisherAPIError( "failed to get table object for: {0}".format(table_name)) deleted_rows = [] for row_id in row_ids: row = db_manager.get_row_by_id(session, table, row_id) if not row: continue if not row.session_has_permissions('d', handler.rpc_session): continue session.delete(row) deleted_rows.append(row_id) session.commit() return deleted_rows
def rpc_events_is_subscribed(handler, event_id, event_type): """ Check if the client is currently subscribed to the specified server event. :param str event_id: The identifier of the event to subscribe to. :param str event_type: A sub-type for the corresponding event. :return: Whether or not the client is subscribed to the event. :rtype: bool """ if not isinstance(event_id, str): raise errors.KingPhisherAPIError('a valid event id must be specified') if not isinstance(event_type, str): raise errors.KingPhisherAPIError('a valid event type must be specified') event_socket = handler.rpc_session.event_socket if event_socket is None: raise errors.KingPhisherAPIError('the event socket is not open for this session') return event_socket.is_subscribed(event_id, event_type)
def rest_api_geoip_lookup(handler, params): ip = handler.get_query('ip') if not ip: logger.error( 'the required \'ip\' parameter is missing for geoip/lookup') raise errors.KingPhisherAPIError( 'the required \'ip\' parameter is missing for geoip/lookup') return geoip.lookup(ip)
def rpc_database_count_rows(handler, session, table_name, query_filter=None): """ Get a count of the rows in the specified table where the search criteria matches. :param str table_name: The name of the database table to query. :param dict query_filter: A dictionary mapping optional search criteria for matching the query. :return: The number of matching rows. :rtype: int """ metatable = database_tables.get(table_name) if not metatable: raise errors.KingPhisherAPIError("failed to get table object for: {0}".format(table_name)) query_filter = query_filter or {} for column in query_filter.keys(): if column not in metatable.column_names: raise errors.KingPhisherAPIError("column {0} is invalid for table {1}".format(column, table_name)) query = session.query(metatable.model) query = query.filter_by(**query_filter) return query.count()
def rpc_events_subscribe(handler, event_id, event_types=None, attributes=None): """ Subscribe the client to the specified event published by the server. When the event is published the specified *attributes* of it and it's corresponding id and type information will be sent to the client. :param str event_id: The identifier of the event to subscribe to. :param list event_types: A list of sub-types for the corresponding event. :param list attributes: A list of attributes of the event object to be sent to the client. """ if not isinstance(event_id, str): raise errors.KingPhisherAPIError('a valid event id must be specified') event_socket = handler.rpc_session.event_socket if event_socket is None: raise errors.KingPhisherAPIError('the event socket is not open for this session') if not event_id.startswith('db-'): # db-<table name> events are the only ones that are valid right now raise errors.KingPhisherAPIError('invalid event_id: ' + event_id) table_name = event_id[3:] table_name = table_name.replace('-', '_') metatable = database_tables.get(table_name) if metatable is None: raise errors.KingPhisherAPIError("invalid table object: {0}".format(table_name)) for event_type in event_types: if event_type not in ('deleted', 'inserted', 'updated'): raise errors.KingPhisherAPIError("event type {0} is invalid for db-* events".format(event_type)) for column in attributes: if column not in metatable.column_names: raise errors.KingPhisherAPIError("column {0} is invalid for table {1}".format(column, table_name)) return event_socket.subscribe(event_id, event_types=event_types, attributes=attributes)
def rpc_database_delete_row_by_id(handler, session, table_name, row_id): """ Delete the row from the table with the specified value in the id column. If the row does not exist, no error is raised. :param str table_name: The name of the database table to delete a row from. :param row_id: The id value. """ metatable = database_tables.get(table_name) if not metatable: raise errors.KingPhisherAPIError("failed to get table object for: {0}".format(table_name)) row = db_manager.get_row_by_id(session, metatable.model, row_id) if row is None: logger = logging.getLogger('KingPhisher.Server.API.RPC') logger.debug("received delete request for non existing row with id {0} from table {1}".format(row_id, table_name)) return row.assert_session_has_permissions('d', handler.rpc_session) session.delete(row) session.commit()
def rpc_database_get_row_by_id(handler, session, table_name, row_id): """ Retrieve a row from a given table with the specified value in the id column. :param str table_name: The name of the database table to retrieve a row from. :param row_id: The id value. :return: The specified row data. :rtype: dict """ table = database_table_objects.get(table_name) if not table: raise errors.KingPhisherAPIError("failed to get table object for: {0}".format(table_name)) columns = database_tables[table_name] row = db_manager.get_row_by_id(session, table, row_id) if row: row.assert_session_has_permissions('r', handler.rpc_session) row = dict(zip(columns, (getattr(row, c) for c in columns))) return row
def rpc_database_get_row_by_id(handler, session, table_name, row_id): """ Retrieve a row from a given table with the specified value in the id column. :param str table_name: The name of the database table to retrieve a row from. :param row_id: The id value. :return: The specified row data. :rtype: dict """ metatable = handler.server.tables_api.get(table_name) if not metatable: raise errors.KingPhisherAPIError("failed to get table object for: {0}".format(table_name)) row = db_manager.get_row_by_id(session, metatable.model, row_id) if row: row.assert_session_has_permissions('r', handler.rpc_session) row = dict(zip(metatable.column_names, (getattr(row, c) for c in metatable.column_names))) elif metatable.model.is_private: raise errors.KingPhisherPermissionError() return row
def rpc_version(handler): """ Get the version information of the server. This returns a dictionary with keys of version, version_info and rpc_api_version. These values are provided for the client to determine compatibility. :return: A dictionary with version information. :rtype: dict """ if not ipaddress.ip_address(handler.client_address[0]).is_loopback: message = "an rpc request to /version was received from non-loopback IP address: {0}".format(handler.client_address[0]) rpc_logger.error(message) raise errors.KingPhisherAPIError(message) vinfo = { 'rpc_api_version': version.rpc_api_version, 'version': version.version, 'version_info': version.version_info._asdict() } return vinfo