def setup_subparser(cls, subparser): """Add the output get subparser: manage.py output get [options]""" outputs = sorted(StreamAlertOutput.get_all_outputs().keys()) get_parser = generate_subparser( subparser, 'get', description=cls.description, help=cls.description, subcommand=True, ) # Add the positional arg of service get_parser.add_argument( 'service', choices=outputs, metavar='SERVICE', help= 'Service to pull configured outputs and their secrets, select from: {}' .format(', '.join(outputs))) # Add the optional ability to pass multiple descriptors get_parser.add_argument( '--descriptors', '-d', nargs="+", default=False, help= 'Pass descriptor and service to pull back the relevant configuration' )
def setup_subparser(cls, subparser): """Add generate-skeleton subparser to the output subparser""" outputs = sorted(StreamAlertOutput.get_all_outputs().keys()) # Create the generate-skeleton parser generate_skeleton_parser = generate_subparser( subparser, 'generate-skeleton', description=cls.description, help=cls.description, subcommand=True) # Add the optional ability to pass services generate_skeleton_parser.add_argument( '--services', choices=outputs, nargs='+', metavar='SERVICE', default=outputs, help= 'Pass the services to generate the skeleton for from services: {}'. format(', '.join(outputs))) # Add the optional file flag generate_skeleton_parser.add_argument( '--file', '-f', default=OUTPUTS_FILE, help='File to write to, relative to the current working directory')
def handler(cls, options, config): """Generate a skeleton file for use with set-from-file Args: options (argparse.Namespace): Basically a namedtuple with the service setting config (StreamAlert.config): The configuration of StreamAlert Returns: bool: False if errors occurred, True otherwise """ skeleton = {} for service in options.services: # Retrieve the proper service class to handle dispatching the alerts of this services # No need to safeguard, as choices are defined on --services output = StreamAlertOutput.get_dispatcher(service) # get dictionary of OutputProperty items to be used for user prompting properties = output.get_user_defined_properties() skeleton[service] = [{ name: 'desc: {}, restrictions: {}'.format(prop.description, prop.input_restrictions) for name, prop in properties.items() }] try: with open(options.file, 'w') as json_file_fp: json.dump(skeleton, json_file_fp, indent=2, sort_keys=True) except Exception as err: # pylint: disable=broad-except LOGGER.error(err) return False LOGGER.info( 'Successfully generated the Skeleton file %s for services: %s', options.file, options.services) return True
def setup_subparser(cls, subparser): """Setup: manage.py output set [options] Args: outputs (list): List of available output services """ outputs = sorted(StreamAlertOutput.get_all_outputs().keys()) set_parser = generate_subparser(subparser, 'set', description=cls.description, help=cls.description, subcommand=True) # Add the required positional arg of service set_parser.add_argument( 'service', choices=outputs, metavar='SERVICE', help= 'Create a new StreamAlert output for one of the available services: {}' .format(', '.join(outputs))) # Add the optional update flag, which allows existing outputs to be updated set_parser.add_argument( '--update', '-u', action='store_true', default=False, help='If the output already exists, overwrite it')
def test_output_loading(): """OutputDispatcher - Loading Output Classes""" loaded_outputs = set(StreamAlertOutput.get_all_outputs()) # Add new outputs to this list to make sure they're loaded properly expected_outputs = { 'aws-firehose', 'aws-lambda', 'aws-s3', 'aws-ses', 'aws-sns', 'aws-sqs', 'aws-cloudwatch-log', 'carbonblack', 'demisto', 'github', 'jira', 'komand', 'pagerduty', 'pagerduty-v2', 'pagerduty-incident', 'phantom', 'slack', 'teams' } assert_count_equal(loaded_outputs, expected_outputs)
def handler(cls, options, config): """Configure multiple outputs for multiple services Args: options (argparse.Namespace): Basically a namedtuple with the service setting config (StreamAlert.config): The configuration of StreamAlert Returns: bool: False if errors occurred, True otherwise """ try: with open(options.file, 'r') as json_file_fp: file_contents = json.load(json_file_fp) except Exception: # pylint: disable=broad-except LOGGER.error("Error opening file %s", options.file) return False if not file_contents: LOGGER.error('File %s is empty', options.file) return False for service, configurations in file_contents.items(): LOGGER.debug('Setting outputs for service %s', service) # Retrieve the proper service class to handle dispatching the alerts of this service output = StreamAlertOutput.get_dispatcher(service) for configuration in configurations: properties = cls.convert_configuration_to_properties( configuration, output) if not properties: # Configuration was not valid return False if not options.update and output_exists( config['outputs'], properties, service): # If the output already exists and update is not set # return early return False # For each configuration for this service, save the creds and update the config if not cls.save_credentials(service, config, properties): return False cls.update_config(options, config, properties, output, service) LOGGER.info('Saved %s configurations for service: %s', len(configurations), service) LOGGER.info('Finished setting all configurations for services: %s', file_contents.keys()) return True
def handler(cls, options, config): """Configure a new output for this service Args: options (argparse.Namespace): Basically a namedtuple with the service setting config (StreamAlert.config): The configuration of StreamAlert Returns: bool: False if errors occurred, True otherwise """ # Retrieve the proper service class to handle dispatching the alerts of this services output = StreamAlertOutput.get_dispatcher(options.service) # If an output for this service has not been defined, the error is logged # prior to this if not output: return False # get dictionary of OutputProperty items to be used for user prompting properties = output.get_user_defined_properties() for name, prop in properties.items(): # pylint: disable=protected-access properties[name] = prop._replace(value=user_input( prop.description, prop.mask_input, prop.input_restrictions)) service = output.__service__ if not options.update and output_exists(config['outputs'], properties, service): # If the output already exists and update is not set # ask for user input again for a unique configuration return cls.handler(options, config) if not cls.save_credentials(service, config, properties): # Error message is already logged so no need to log a new one return False cls.update_config(options, config, properties, output, service) LOGGER.info( 'Successfully saved \'%s\' output configuration for service \'%s\'', properties['descriptor'].value, service) return True
def setup_subparser(cls, subparser): """Add the output list subparser: manage.py output list [options]""" outputs = sorted(StreamAlertOutput.get_all_outputs().keys()) list_parser = generate_subparser( subparser, 'list', description=cls.description, help=cls.description, subcommand=True, ) # Add the optional arg of service list_parser.add_argument( '--service', '-s', choices=outputs, default=outputs, nargs='*', metavar='SERVICE', help= 'Pass Services to list configured output descriptors, select from: {}' .format(', '.join(outputs)))
def handler(cls, options, config): """Fetches the configuration for a service Args: options (argparse.Namespace): Basically a namedtuple with the service setting config (StreamAlert.config): The configuration of StreamAlert Returns: bool: False if errors occurred, True otherwise """ service = options.service output = StreamAlertOutput.create_dispatcher(service, config) # Get the descriptors for the service. No need to check service # as this is handled by argparse choices configured_descriptors = [ descriptor for descriptor in config["outputs"][service] if 'sample' not in descriptor ] # Set the descriptors to get the secrets for descriptors = options.descriptors if options.descriptors else configured_descriptors LOGGER.debug('Getting secrets for service %s and descriptors %s', service, descriptors) credentials = [] for descriptor in descriptors: if descriptor not in configured_descriptors: LOGGER.error('Invalid descriptor %s, it doesn\'t exist', descriptor) return False creds = output._load_creds(descriptor) # pylint: disable=protected-access creds['descriptor'] = descriptor credentials.append(creds) print('\nService Name:', service) print(json.dumps(credentials, indent=2, sort_keys=True), '\n')
def _create_dispatcher(self, output): """Create a dispatcher for the given output. Args: output (str): Alert output, e.g. "aws-sns:topic-name" Returns: OutputDispatcher: Based on the output type. Returns None if the output is invalid or not defined in the config. """ try: service, descriptor = output.split(':') except ValueError: LOGGER.error( 'Improperly formatted output [%s]. Outputs for rules must ' 'be declared with both a service and a descriptor for the ' 'integration (ie: \'slack:my_channel\')', output) return None if service not in self.config or descriptor not in self.config[service]: LOGGER.error('The output \'%s\' does not exist!', output) return None return StreamAlertOutput.create_dispatcher(service, self.config)
def test_user_defined_properties(): """OutputDispatcher - User Defined Properties""" for output in list(StreamAlertOutput.get_all_outputs().values()): props = output.get_user_defined_properties() # The user defined properties should at a minimum contain a descriptor assert_is_not_none(props.get('descriptor'))
def test_create_dispatcher(): """StreamAlertOutput - Create Dispatcher""" dispatcher = StreamAlertOutput.create_dispatcher('aws-s3', CONFIG) assert_is_instance(dispatcher, S3Output)
def test_get_dispatcher_bad(log_mock): """StreamAlertOutput - Get Invalid Dispatcher""" dispatcher = StreamAlertOutput.get_dispatcher('aws-s4') assert_is_none(dispatcher) log_mock.assert_called_with('Designated output service [%s] does not exist', 'aws-s4')
def test_get_dispatcher_good(): """StreamAlertOutput - Get Valid Dispatcher""" dispatcher = StreamAlertOutput.get_dispatcher('aws-s3') assert_is_not_none(dispatcher)