Example #1
0
	def test_server_config_verification(self):
		config_file = find.data_file('server_config.yml')
		self.assertIsNotNone(config_file)
		config_schema = find.data_file(os.path.join('schemas', 'json', 'king-phisher.server.config.json'))
		self.assertIsNotNone(config_schema)
		config = configuration.Configuration.from_file(config_file)
		self.assertIsEmpty(config.schema_errors(config_schema))
	def test_server_config_verification(self):
		config_file = find.data_file('server_config.yml')
		verify_config_file = find.data_file('server_config_verification.yml')
		self.assertIsNotNone(config_file)
		config = configuration.Configuration(config_file)
		bad_options = config.get_missing(verify_config_file)
		self.assertIsInstance(bad_options, dict)
		incompatible_options = bad_options.get('incompatible')
		self.assertFalse(bool(incompatible_options), msg='an option is of an invalid type in the server config template')
		missing_options = bad_options.get('missing')
		self.assertFalse(bool(missing_options), msg='an option is missing in the server config template')
Example #3
0
	def __init__(self, *args, **kwargs):
		super(CampaignViewDashboardTab, self).__init__(*args, **kwargs)
		self.graphs = []
		"""The :py:class:`.CampaignGraph` classes represented on the dash board."""

		dash_ports = {
			# dashboard position, (width, height)
			'top_left': (380, 200),
			'top_right': (380, 200),
			'bottom': (760, 200)
		}
		for dash_port, details in dash_ports.items():
			graph_name = self.config['dashboard.' + dash_port]
			cls = graphs.get_graph(graph_name)
			if not cls:
				self.logger.warning('could not get graph: ' + graph_name)
				logo_file_path = find.data_file('king-phisher-icon.svg')
				if logo_file_path:
					image = Gtk.Image.new_from_pixbuf(GdkPixbuf.Pixbuf.new_from_file_at_size(logo_file_path, 128, 128))
					image.show()
					self.gobjects['scrolledwindow_' + dash_port].add(image)
				continue
			graph_inst = cls(self.application, details, getattr(self, self.top_gobject).get_style_context())
			self.gobjects['scrolledwindow_' + dash_port].add(graph_inst.canvas)
			self.gobjects['box_' + dash_port].pack_end(graph_inst.navigation_toolbar, False, False, 0)
			self.graphs.append(graph_inst)
		self.logger.debug("dashboard refresh frequency set to {0} seconds".format(self.refresh_frequency))
		GLib.timeout_add_seconds(self.refresh_frequency, self.loader_idle_routine)
Example #4
0
	def _create_config(self):
		config_dir = os.path.dirname(self.config_file)
		if not os.path.isdir(config_dir):
			self.logger.debug('creating the user configuration directory')
			os.makedirs(config_dir)
		client_template = find.data_file('client_config.json')
		shutil.copy(client_template, self.config_file)
Example #5
0
	def __init__(self, *args, **kwargs):
		super(AboutDialog, self).__init__(*args, **kwargs)
		logo_file_path = find.data_file('king-phisher-icon.svg')
		if logo_file_path:
			logo_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(logo_file_path, 128, 128)
			self.dialog.set_property('logo', logo_pixbuf)
		self.dialog.set_property('version', version.version)
		self.dialog.connect('activate-link', lambda _, url: utilities.open_uri(url))
Example #6
0
	def get_source(self, environment, template):
		template_path = find.data_file(template, os.R_OK)
		if template_path is None:
			raise jinja2.TemplateNotFound(template)
		mtime = os.path.getmtime(template_path)
		with codecs.open(template_path, 'r', encoding='utf-8') as file_h:
			source = file_h.read()
		return source, template_path, lambda: mtime == os.path.getmtime(template_path)
def which_glade():
	"""
	Locate the glade data file which stores the UI information in a Gtk Builder
	format.

	:return: The path to the glade data file.
	:rtype: str
	"""
	return find.data_file(os.environ.get('KING_PHISHER_GLADE_FILE', 'king-phisher-client.ui'))
Example #8
0
	def test_client_template_config(self):
		find.data_path_append('data/client')
		config_h = open(find.data_file('client_config.json'))
		try:
			json.load(config_h)
		except Exception:
			self.fail("failed to parse the client JSON configuration file")
		finally:
			config_h.close()
Example #9
0
	def test_schema(self):
		file_path = find.data_file('security.json')
		self.assertIsNotNone(file_path, msg='failed to find the security.json file')
		with open(file_path, 'r') as file_h:
			key_store = serializers.JSON.load(file_h)
		try:
			utilities.validate_json_schema(key_store, 'king-phisher.security')
		except jsonschema.ValidationError:
			self.fail('the security.json file failed validation')
Example #10
0
	def test_server_config(self):
		config_file = find.data_file('server_config.yml')
		self.assertIsNotNone(config_file)
		self.assertTrue(os.path.isfile(config_file))
		config = configuration.Configuration.from_file(config_file)
		self.assertTrue(config.has_section('server'))
		self.assertTrue(config.has_option('server.addresses'))
		addresses = config.get('server.addresses')
		self.assertIsInstance(addresses, list)
		self.assertGreater(len(addresses), 0)
	def __init__(self):
		super(CustomCompletionProviderBase, self).__init__()
		self.logger = logging.getLogger('KingPhisher.Client.' + self.__class__.__name__)
		if self.data_file is not None:
			completion_data = find.data_file(os.path.join('completion', self.data_file))
			if completion_data is None:
				raise RuntimeError("failed to find completion data file '{0}'".format(self.data_file))
			self.logger.debug("loading {0} completion data from: {1}".format(self.name, completion_data))
			with open(completion_data, 'r') as file_h:
				completion_data = serializers.JSON.load(file_h)
			self.load_data(completion_data)
Example #12
0
	def _init_tables_api(self):
		# initialize the tables api dataset, this is to effectively pin the schema exposed allowing new columns to be
		# added without breaking rpc compatibility
		file_path = find.data_file('table-api.json')
		if file_path is None:
			raise errors.KingPhisherResourceError('missing the table-api.json data file')
		with open(file_path, 'r') as file_h:
			tables_api_data = serializers.JSON.load(file_h)
		if tables_api_data['schema'] > db_models.SCHEMA_VERSION:
			raise errors.KingPhisherInputValidationError('the table-api.json data file\'s schema version is incompatible')
		for table, columns in tables_api_data['tables'].items():
			self.tables_api[table] = db_models.MetaTable(column_names=columns, model=db_models.database_tables[table].model, name=table)
		self.logger.debug("initialized the table api dataset (schema version: {0})".format(tables_api_data['schema']))
Example #13
0
	def respond_not_found(self):
		self.send_response(404, 'Not Found')
		self.send_header('Content-Type', 'text/html')
		page_404 = find.data_file('error_404.html')
		if page_404:
			with open(page_404, 'rb') as file_h:
				message = file_h.read()
		else:
			message = b'Resource Not Found\n'
		self.send_header('Content-Length', len(message))
		self.end_headers()
		self.wfile.write(message)
		return
Example #14
0
	def respond_not_found(self):
		self.send_response(404, 'Not Found')
		self.send_header('Content-Type', 'text/html; charset=utf-8')
		file_path = find.data_file(os.path.join('pages', 'error_404.html'))
		if file_path:
			with open(file_path, 'rb') as file_h:
				message = file_h.read()
		else:
			message = b'Resource Not Found\n'
		self.send_header('Content-Length', len(message))
		self.end_headers()
		self.wfile.write(message)
		return
Example #15
0
def validate_json_schema(data, schema_file_id):
	"""
	Validate the specified data against the specified schema. The schema file
	will be searched for and loaded based on it's id. If the validation fails
	a :py:class:`~jsonschema.exceptions.ValidationError` will be raised.

	:param data: The data to validate against the schema.
	:param schema_file_id: The id of the schema to load.
	"""
	schema_file_name = schema_file_id + '.json'
	file_path = find.data_file(os.path.join('schemas', 'json', schema_file_name))
	with open(file_path, 'r') as file_h:
		schema = json.load(file_h)
	jsonschema.validate(data, schema)
Example #16
0
	def graphql_find_file(self, query_file, **query_vars):
		"""
		This method is similar to :py:meth:`~.graphql_file`. The first argument
		(*query_file*) is the name of a query file that will be located using
		:py:func:`find.data_file`. Additional keyword arguments are passed as
		the variables to the query.

		:param str query_file: The name of the query file to locate.
		:param query_vars: These keyword arguments are passed as the variables to the query.
		:return: The query results.
		:rtype: dict
		"""
		path = find.data_file(os.path.join('queries', query_file))
		if path is None:
			raise errors.KingPhisherResourceError('could not find GraphQL query file: ' + query_file)
		return self.graphql_file(path, query_vars=query_vars)
Example #17
0
	def do_config_load(self, load_defaults):
		"""
		Load the client configuration from disk and set the
		:py:attr:`~.KingPhisherClientApplication.config` attribute.

		:param bool load_defaults: Load missing options from the template configuration file.
		"""
		client_template = find.data_file('client_config.json')
		self.logger.info('loading the config from: ' + self.config_file)
		with open(self.config_file, 'r') as tmp_file:
			self.config = serializers.JSON.load(tmp_file)
		if load_defaults:
			with open(client_template, 'r') as tmp_file:
				client_template = serializers.JSON.load(tmp_file)
			for key, value in client_template.items():
				if not key in self.config:
					self.config[key] = value
Example #18
0
    def do_config_load(self, load_defaults):
        """
		Load the client configuration from disk and set the
		:py:attr:`~.KingPhisherClientApplication.config` attribute.

		:param bool load_defaults: Load missing options from the template configuration file.
		"""
        client_template = find.data_file('client_config.json')
        self.logger.info('loading the config from: ' + self.config_file)
        with open(self.config_file, 'r') as tmp_file:
            self.config = serializers.JSON.load(tmp_file)
        if load_defaults:
            with open(client_template, 'r') as tmp_file:
                client_template = serializers.JSON.load(tmp_file)
            for key, value in client_template.items():
                if not key in self.config:
                    self.config[key] = value
Example #19
0
    def graphql_find_file(self, query_file, **query_vars):
        """
		This method is similar to :py:meth:`~.graphql_file`. The first argument
		(*query_file*) is the name of a query file that will be located using
		:py:func:`find.data_file`. Additional keyword arguments are passed as
		the variables to the query.

		:param str query_file: The name of the query file to locate.
		:param query_vars: These keyword arguments are passed as the variables to the query.
		:return: The query results.
		:rtype: dict
		"""
        path = find.data_file(os.path.join('queries', query_file))
        if path is None:
            raise errors.KingPhisherResourceError(
                'could not find GraphQL query file: ' + query_file)
        return self.graphql_file(path, query_vars=query_vars)
Example #20
0
def validate_json_schema(data, schema_file_id):
    """
	Validate the specified data against the specified schema. The schema file
	will be searched for and loaded based on it's id. If the validation fails
	a :py:class:`~jsonschema.exceptions.ValidationError` will be raised.

	:param data: The data to validate against the schema.
	:param schema_file_id: The id of the schema to load.
	"""
    schema_file_name = schema_file_id + '.json'
    file_path = find.data_file(
        os.path.join('schemas', 'json', schema_file_name))
    if file_path is None:
        raise FileNotFoundError('the json schema file was not found')
    with open(file_path, 'r') as file_h:
        schema = json.load(file_h)
    jsonschema.validate(data, schema)
Example #21
0
 def load_campaigns(self, cursor=None):
     """Load campaigns from the remote server and populate the :py:class:`Gtk.TreeView`."""
     if cursor is None:
         self._tv_model.clear()
         self.gobjects['revealer_loading'].set_reveal_child(True)
         self.gobjects['progressbar_loading'].set_fraction(0.0)
     path = find.data_file(os.path.join('queries', 'get_campaigns.graphql'))
     if path is None:
         raise errors.KingPhisherResourceError(
             'could not find GraphQL query file: get_campaigns.graphql')
     self.application.rpc.async_graphql_file(
         path,
         query_vars={
             'cursor': cursor,
             'page': _QUERY_PAGE_SIZE
         },
         on_success=self.__async_rpc_cb_load_campaigns,
         when_idle=True)
Example #22
0
def ex_load_config(config_file, validate_schema=True):
	"""
	Load the server configuration from the specified file. This function is
	meant to be called early on during a scripts execution and if any error
	occurs, details will be printed and the process will exit.

	:param str config_file: The path to the configuration file to load.
	:param bool validate_schema: Whether or not to validate the schema of the configuration.
	:return: The loaded server configuration.
	:rtype: :py:class:`.Configuration`
	"""
	try:
		config = Configuration.from_file(config_file)
	except Exception as error:
		color.print_error('an error occurred while parsing the server configuration file')
		if isinstance(error, yaml.error.YAMLError):
			problem = getattr(error, 'problem', 'unknown yaml error')
			if hasattr(error, 'problem_mark'):
				prob_lineno = error.problem_mark.line + 1
				color.print_error("{0} - {1}:{2} {3}".format(error.__class__.__name__, config_file, prob_lineno, problem))
				lines = open(config_file, 'rU').readlines()
				for lineno, line in enumerate(lines[max(prob_lineno - 3, 0):(prob_lineno + 2)], max(prob_lineno - 3, 0) + 1):
					color.print_error("  {0} {1: <3}: {2}".format(('=>' if lineno == prob_lineno else '  '), lineno, line.rstrip()))
			else:
				color.print_error("{0} - {1}: {2}".format(error.__class__.__name__, config_file, problem))
		color.print_error('fix the errors in the configuration file and restart the server')
		sys.exit(os.EX_CONFIG)

	# check the configuration for missing and incompatible options
	if validate_schema:
		find.init_data_path('server')
		config_schema = find.data_file(os.path.join('schemas', 'json', 'king-phisher.server.config.json'))
		if not config_schema:
			color.print_error('could not load server configuration schema data')
			sys.exit(os.EX_NOINPUT)

		schema_errors = config.schema_errors(config_schema)
		if schema_errors:
			color.print_error('the server configuration validation encountered the following errors:')
			for schema_error in schema_errors:
				color.print_error("  - {0} error: {1} ({2})".format(schema_error.validator, '.'.join(map(str, schema_error.path)), schema_error.message))
			sys.exit(os.EX_CONFIG)
	return config
Example #23
0
    def do_config_load(self, load_defaults):
        """
		Load the client configuration from disk and set the
		:py:attr:`~.KingPhisherClientApplication.config` attribute.

		Check the proxy environment variable and set them appropriately.

		:param bool load_defaults: Load missing options from the template configuration file.
		"""
        client_template = find.data_file('client_config.json')
        self.logger.info('loading the config from: ' + self.config_file)
        with open(self.config_file, 'r') as tmp_file:
            self.config = serializers.JSON.load(tmp_file)
        if load_defaults:
            with open(client_template, 'r') as tmp_file:
                client_template = serializers.JSON.load(tmp_file)
            for key, value in client_template.items():
                if not key in self.config:
                    self.config[key] = value
        env_proxy = os.environ.get('HTTPS_PROXY')
        if env_proxy is not None:
            env_proxy = env_proxy.strip()
            proxy_url = urllib.parse.urlparse(env_proxy)
            if not (proxy_url.hostname and proxy_url.scheme):
                self.logger.error(
                    'invalid proxy url (missing scheme or hostname)')
                return
            if self.config[
                    'proxy.url'] and env_proxy != self.config['proxy.url']:
                self.logger.warning(
                    'setting proxy configuration via the environment, overriding the configuration'
                )
            else:
                self.logger.info(
                    'setting proxy configuration via the environment')
            self.config['proxy.url'] = env_proxy
        elif self.config['proxy.url']:
            self.logger.info(
                'setting proxy configuration via the configuration')
            os.environ['HTTPS_PROXY'] = self.config['proxy.url']
            os.environ['HTTP_PROXY'] = self.config['proxy.url']
        else:
            os.environ.pop('HTTP_PROXY', None)
Example #24
0
	def _init_tables_api(self):
		# initialize the tables api dataset, this is to effectively pin the schema exposed allowing new columns to be
		# added without breaking rpc compatibility
		file_path = find.data_file('table-api.json')
		if file_path is None:
			raise errors.KingPhisherResourceError('missing the table-api.json data file')
		with open(file_path, 'r') as file_h:
			tables_api_data = serializers.JSON.load(file_h)
		if tables_api_data['schema'] > db_models.SCHEMA_VERSION:
			raise errors.KingPhisherInputValidationError('the table-api.json data file\'s schema version is incompatible')
		for table_name, columns in tables_api_data['tables'].items():
			model = db_models.database_tables[table_name].model
			self.tables_api[table_name] = db_models.MetaTable(
				column_names=columns,
				model=model,
				name=table_name,
				table=model.__table__
			)
		self.logger.debug("initialized the table api dataset (schema version: {0})".format(tables_api_data['schema']))
Example #25
0
	def _load_key_store(self, file_name):
		file_path = find.data_file(file_name)
		if not file_path:
			return 0
		with open(file_path, 'r') as file_h:
			key_store = serializers.JSON.load(file_h)
		utilities.validate_json_schema(key_store, 'king-phisher.security')
		key_store = key_store['keys']
		loaded = 0
		for key_idx, key in enumerate(key_store, 1):
			identifier = key['id']
			if identifier in self.keys:
				self.logger.warning("skipping loading {0}:{1} due to a duplicate id".format(file_name, key_idx))
				continue
			verifying_key = key['verifying-key']
			key['verifying-key'] = VerifyingKey.from_dict(verifying_key, encoding=verifying_key.pop('encoding', 'base64'))
			self.keys[identifier] = key
			self.logger.debug("loaded key id: {0} from: {1}".format(identifier, file_path))
			loaded += 1
		return loaded
    def load_default_config(self):
        """
		Load the default configuration to use when settings are missing. This
		will load the user's configured defaults and fail back to the core ones
		distributed with the application.

		:return: The default configuration.
		:rtype: dict
		"""
        default_client_config = find.data_file('client_config.json')
        with open(default_client_config, 'r') as tmp_file:
            default_client_config = serializers.JSON.load(tmp_file)

        users_defaults = self.storage['default']
        for key, value in users_defaults.items():
            if not is_managed_key(key):
                continue
            if key not in default_client_config:
                continue
            default_client_config[key] = value
        return default_client_config
Example #27
0
	def handle_javascript_hook(self, query):
		kp_hook_js = find.data_file('javascript_hook.js')
		if not kp_hook_js:
			self.respond_not_found()
			return
		with open(kp_hook_js, 'r') as kp_hook_js:
			javascript = kp_hook_js.read()
		if self.config.has_option('beef.hook_url'):
			javascript += "\nloadScript('{0}');\n\n".format(self.config.get('beef.hook_url'))
		self.send_response(200)
		self.send_header('Content-Type', 'text/javascript')
		self.send_header('Pragma', 'no-cache')
		self.send_header('Cache-Control', 'no-cache')
		self.send_header('Expires', '0')
		self.send_header('Access-Control-Allow-Origin', '*')
		self.send_header('Access-Control-Allow-Methods', 'POST, GET')
		self.send_header('Content-Length', len(javascript))
		self.end_headers()
		if not isinstance(javascript, bytes):
			javascript = javascript.encode('utf-8')
		self.wfile.write(javascript)
		return
Example #28
0
	def handle_javascript_hook(self, query):
		kp_hook_js = find.data_file('javascript_hook.js')
		if not kp_hook_js:
			self.respond_not_found()
			return
		with open(kp_hook_js, 'r') as kp_hook_js:
			javascript = kp_hook_js.read()
		if self.config.has_option('beef.hook_url'):
			javascript += "\nloadScript('{0}');\n\n".format(self.config.get('beef.hook_url'))
		self.send_response(200)
		self.send_header('Content-Type', 'text/javascript')
		self.send_header('Content-Length', len(javascript))
		self.send_header('Pragma', 'no-cache')
		self.send_header('Cache-Control', 'no-cache')
		self.send_header('Expires', '0')
		self.send_header('Access-Control-Allow-Origin', '*')
		self.send_header('Access-Control-Allow-Methods', 'POST, GET')
		self.end_headers()
		if not isinstance(javascript, bytes):
			javascript = javascript.encode('utf-8')
		self.wfile.write(javascript)
		return
Example #29
0
	def setUp(self):
		find.data_path_append('data/server')
		web_root = os.path.join(os.getcwd(), 'data', 'server', 'king_phisher')
		server_address = {'host': '127.0.0.1', 'port': 0, 'ssl': False}
		config = configuration.Configuration.from_file(find.data_file('server_config.yml'))
		config.set('server.addresses', [server_address])
		config.set('server.database', 'sqlite://')
		config.set('server.geoip.database', os.environ.get('KING_PHISHER_TEST_GEOIP_DB', './GeoLite2-City.mmdb'))
		config.set('server.web_root', web_root)
		config.set('server.rest_api.enabled', True)
		config.set('server.rest_api.token', rest_api.generate_token())
		self.config = config
		self.plugin_manager = plugins.ServerPluginManager(config)
		self.server = build.server_from_config(config, handler_klass=KingPhisherRequestHandlerTest, plugin_manager=self.plugin_manager)
		server_address['port'] = self.server.sub_servers[0].server_port
		self.assertIsInstance(self.server, server.KingPhisherServer)
		self.server_thread = threading.Thread(target=self.server.serve_forever)
		self.server_thread.daemon = True
		self.server_thread.start()
		self.assertTrue(self.server_thread.is_alive())
		self.shutdown_requested = False
		self.rpc = client_rpc.KingPhisherRPCClient(('localhost', server_address['port']))
		self.rpc.login(username='******', password='******')
Example #30
0
	def setUp(self):
		find.data_path_append('data/server')
		web_root = os.path.join(os.getcwd(), 'data', 'server', 'king_phisher')
		server_address = {'host': '127.0.0.1', 'port': 0, 'ssl': False}
		config = configuration.Configuration.from_file(find.data_file('server_config.yml'))
		config.set('server.addresses', [server_address])
		config.set('server.database', 'sqlite://')
		config.set('server.geoip.database', os.environ.get('KING_PHISHER_TEST_GEOIP_DB', './GeoLite2-City.mmdb'))
		config.set('server.web_root', web_root)
		config.set('server.rest_api.enabled', True)
		config.set('server.rest_api.token', rest_api.generate_token())
		self.config = config
		self.plugin_manager = plugins.ServerPluginManager(config)
		self.server = build.server_from_config(config, handler_klass=KingPhisherRequestHandlerTest, plugin_manager=self.plugin_manager)
		server_address['port'] = self.server.sub_servers[0].server_port
		self.assertIsInstance(self.server, server.KingPhisherServer)
		self.server_thread = threading.Thread(target=self.server.serve_forever)
		self.server_thread.daemon = True
		self.server_thread.start()
		self.assertTrue(self.server_thread.is_alive())
		self.shutdown_requested = False
		self.rpc = client_rpc.KingPhisherRPCClient(('localhost', server_address['port']))
		self.rpc.login(username='******', password='******')
Example #31
0
    def __init__(self, *args, **kwargs):
        super(CampaignViewDashboardTab, self).__init__(*args, **kwargs)
        self.graphs = []
        """The :py:class:`.CampaignGraph` classes represented on the dash board."""

        dash_ports = {
            # dashboard position, (width, height)
            'top_left': (380, 200),
            'top_right': (380, 200),
            'bottom': (760, 200)
        }
        for dash_port, details in dash_ports.items():
            graph_name = self.config['dashboard.' + dash_port]
            cls = graphs.get_graph(graph_name)
            if not cls:
                self.logger.warning('could not get graph: ' + graph_name)
                logo_file_path = find.data_file('king-phisher-icon.svg')
                if logo_file_path:
                    image = Gtk.Image.new_from_pixbuf(
                        GdkPixbuf.Pixbuf.new_from_file_at_size(
                            logo_file_path, 128, 128))
                    image.show()
                    self.gobjects['scrolledwindow_' + dash_port].add(image)
                continue
            graph_inst = cls(
                self.application, details,
                getattr(self, self.top_gobject).get_style_context())
            self.gobjects['scrolledwindow_' + dash_port].add(graph_inst.canvas)
            self.gobjects['box_' + dash_port].pack_end(
                graph_inst.navigation_toolbar, False, False, 0)
            self.graphs.append(graph_inst)
        self.logger.debug(
            "dashboard refresh frequency set to {0} seconds".format(
                self.refresh_frequency))
        GLib.timeout_add_seconds(self.refresh_frequency,
                                 self.loader_idle_routine)
Example #32
0
    def __init__(self, config, application):
        """
		:param dict config: The main King Phisher client configuration.
		:param application: The application instance to which this window belongs.
		:type application: :py:class:`.KingPhisherClientApplication`
		"""
        utilities.assert_arg_type(application, Gtk.Application, arg_pos=2)
        super(MainAppWindow, self).__init__(application=application)
        self.application = application
        self.logger = logging.getLogger('KingPhisher.Client.MainWindow')
        self.config = config
        """The main King Phisher client configuration."""
        self.set_property('title', 'King Phisher')
        vbox = Gtk.Box()
        vbox.set_property('orientation', Gtk.Orientation.VERTICAL)
        vbox.show()
        self.add(vbox)

        default_icon_file = find.data_file('king-phisher-icon.svg')
        if default_icon_file:
            icon_pixbuf = GdkPixbuf.Pixbuf.new_from_file(default_icon_file)
            self.set_default_icon(icon_pixbuf)
        self.accel_group = Gtk.AccelGroup()
        self.add_accel_group(self.accel_group)

        self.menu_bar = MainMenuBar(application, self)
        vbox.pack_start(self.menu_bar.menubar, False, False, 0)

        # create notebook and tabs
        self.notebook = Gtk.Notebook()
        """The primary :py:class:`Gtk.Notebook` that holds the top level taps of the client GUI."""
        self.notebook.connect('switch-page', self.signal_notebook_switch_page)
        self.notebook.set_scrollable(True)
        vbox.pack_start(self.notebook, True, True, 0)

        self.tabs = {}
        current_page = self.notebook.get_current_page()
        self.last_page_id = current_page

        mailer_tab = MailSenderTab(self, self.application)
        self.tabs['mailer'] = mailer_tab
        self.notebook.insert_page(mailer_tab.box, mailer_tab.label,
                                  current_page + 1)
        self.notebook.set_current_page(current_page + 1)

        campaign_tab = CampaignViewTab(self, self.application)
        campaign_tab.box.show()
        self.tabs['campaign'] = campaign_tab
        self.notebook.insert_page(campaign_tab.box, campaign_tab.label,
                                  current_page + 2)

        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
        self.set_size_request(800, 600)
        self.connect('delete-event', self.signal_delete_event)
        self.notebook.show()

        self.show()
        self.rpc = None  # needs to be initialized last
        """The :py:class:`.KingPhisherRPCClient` instance."""

        self.application.connect('server-connected',
                                 self.signal_kp_server_connected)

        self.login_dialog = dialogs.LoginDialog(self.application)
        self.login_dialog.dialog.connect('response',
                                         self.signal_login_dialog_response)
        self.login_dialog.show()
Example #33
0
 def setUp(self):
     find.data_path_append('data/client')
     builder_xml = find.data_file('king-phisher-client.ui')
     self.xml_tree = ElementTree.parse(builder_xml)
     self.xml_root = self.xml_tree.getroot()
Example #34
0
	def theme_file(self):
		if not self._theme_file:
			return DISABLED
		return find.data_file(os.path.join('style', self._theme_file))
Example #35
0
 def theme_file(self):
     if not self._theme_file:
         return DISABLED
     return find.data_file(os.path.join('style', self._theme_file))
Example #36
0
def init_database(connection_url, extra_init=False):
    """
	Create and initialize the database engine. This must be done before the
	session object can be used. This will also attempt to perform any updates to
	the database schema if the backend supports such operations.

	:param str connection_url: The url for the database connection.
	:param bool extra_init: Run optional extra dbms-specific initialization logic.
	:return: The initialized database engine.
	"""
    connection_url = normalize_connection_url(connection_url)
    connection_url = sqlalchemy.engine.url.make_url(connection_url)
    logger.info("initializing database connection with driver {0}".format(
        connection_url.drivername))
    if connection_url.drivername == 'sqlite':
        engine = sqlalchemy.create_engine(
            connection_url,
            connect_args={'check_same_thread': False},
            poolclass=sqlalchemy.pool.StaticPool)
        sqlalchemy.event.listens_for(
            engine, 'begin')(lambda conn: conn.execute('BEGIN'))
    elif connection_url.drivername == 'postgresql':
        if extra_init:
            init_database_postgresql(connection_url)
        engine = sqlalchemy.create_engine(
            connection_url, connect_args={'client_encoding': 'utf8'})
    else:
        raise errors.KingPhisherDatabaseError(
            'only sqlite and postgresql database drivers are supported')

    Session.remove()
    Session.configure(bind=engine)
    inspector = sqlalchemy.inspect(engine)
    if 'campaigns' not in inspector.get_table_names():
        logger.debug('campaigns table not found, creating all new tables')
        try:
            models.Base.metadata.create_all(engine)
        except sqlalchemy.exc.SQLAlchemyError as error:
            error_lines = (line.strip() for line in error.message.split('\n'))
            raise errors.KingPhisherDatabaseError(
                'SQLAlchemyError: ' + ' '.join(error_lines).strip())

    schema_version = get_schema_version(engine)
    logger.debug("current database schema version: {0} ({1})".format(
        schema_version,
        ('latest' if schema_version == models.SCHEMA_VERSION else 'obsolete')))
    if 'alembic_version' not in inspector.get_table_names():
        logger.debug(
            'alembic version table not found, attempting to create and set version'
        )
        init_alembic(engine, schema_version)

    if schema_version > models.SCHEMA_VERSION:
        raise errors.KingPhisherDatabaseError(
            'the database schema is for a newer version, automatic downgrades are not supported'
        )
    elif schema_version < models.SCHEMA_VERSION:
        alembic_config_file = find.data_file('alembic.ini')
        if not alembic_config_file:
            raise errors.KingPhisherDatabaseError(
                'cannot find the alembic.ini configuration file')
        alembic_directory = find.data_directory('alembic')
        if not alembic_directory:
            raise errors.KingPhisherDatabaseError(
                'cannot find the alembic data directory')

        config = alembic.config.Config(alembic_config_file)
        config.config_file_name = alembic_config_file
        config.set_main_option('script_location', alembic_directory)
        config.set_main_option('skip_logger_config', 'True')
        config.set_main_option('sqlalchemy.url', str(connection_url))

        logger.warning(
            "automatically updating the database schema from version {0} to {1}"
            .format(schema_version, models.SCHEMA_VERSION))
        try:
            alembic.command.upgrade(config, 'head')
        except Exception as error:
            logger.critical(
                "database schema upgrade failed with exception: {0}.{1} {2}".
                format(error.__class__.__module__, error.__class__.__name__,
                       getattr(error, 'message', '')).rstrip(),
                exc_info=True)
            raise errors.KingPhisherDatabaseError(
                'failed to upgrade to the latest database schema')
        logger.info(
            "successfully updated the database schema from version {0} to {1}".
            format(schema_version, models.SCHEMA_VERSION))
        # reset it because it may have been altered by alembic
        Session.remove()
        Session.configure(bind=engine)
    set_metadata('database_driver', connection_url.drivername)
    set_metadata('last_started', datetime.datetime.utcnow())
    set_metadata('schema_version', models.SCHEMA_VERSION)

    logger.debug("connected to {0} database: {1}".format(
        connection_url.drivername, connection_url.database))
    signals.db_initialized.send(connection_url)
    return engine
Example #37
0
	def __init__(self, config, application):
		"""
		:param dict config: The main King Phisher client configuration.
		:param application: The application instance to which this window belongs.
		:type application: :py:class:`.KingPhisherClientApplication`
		"""
		utilities.assert_arg_type(application, Gtk.Application, arg_pos=2)
		super(MainAppWindow, self).__init__(application=application)
		self.application = application
		self.logger = logging.getLogger('KingPhisher.Client.MainWindow')
		self.config = config
		"""The main King Phisher client configuration."""
		self.set_property('title', 'King Phisher')
		vbox = Gtk.Box()
		vbox.set_property('orientation', Gtk.Orientation.VERTICAL)
		vbox.show()
		self.add(vbox)

		default_icon_file = find.data_file('king-phisher-icon.svg')
		if default_icon_file:
			icon_pixbuf = GdkPixbuf.Pixbuf.new_from_file(default_icon_file)
			self.set_default_icon(icon_pixbuf)
		self.accel_group = Gtk.AccelGroup()
		self.add_accel_group(self.accel_group)

		self.menu_bar = MainMenuBar(application, self)
		vbox.pack_start(self.menu_bar.menubar, False, False, 0)

		# create notebook and tabs
		self.notebook = Gtk.Notebook()
		"""The primary :py:class:`Gtk.Notebook` that holds the top level taps of the client GUI."""
		self.notebook.connect('switch-page', self.signal_notebook_switch_page)
		self.notebook.set_scrollable(True)
		vbox.pack_start(self.notebook, True, True, 0)

		self.tabs = {}
		current_page = self.notebook.get_current_page()
		self.last_page_id = current_page

		mailer_tab = MailSenderTab(self, self.application)
		self.tabs['mailer'] = mailer_tab
		self.notebook.insert_page(mailer_tab.box, mailer_tab.label, current_page + 1)
		self.notebook.set_current_page(current_page + 1)

		campaign_tab = CampaignViewTab(self, self.application)
		campaign_tab.box.show()
		self.tabs['campaign'] = campaign_tab
		self.notebook.insert_page(campaign_tab.box, campaign_tab.label, current_page + 2)

		self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
		self.set_size_request(800, 600)
		self.connect('delete-event', self.signal_delete_event)
		self.notebook.show()

		self.show()
		self.rpc = None  # needs to be initialized last
		"""The :py:class:`.KingPhisherRPCClient` instance."""

		self.application.connect('server-connected', self.signal_kp_server_connected)

		self.login_dialog = dialogs.LoginDialog(self.application)
		self.login_dialog.dialog.connect('response', self.signal_login_dialog_response)
		self.login_dialog.show()
Example #38
0
def _graphql_find_file(query_file):
    path = find.data_file(os.path.join('queries', query_file))
    if path is None:
        raise errors.KingPhisherResourceError(
            'could not find GraphQL query file: ' + query_file)
    return _graphql_file(path)
Example #39
0
	def setUp(self):
		find.data_path_append('data/client')
		builder_xml = find.data_file('king-phisher-client.ui')
		self.xml_tree = ElementTree.parse(builder_xml)
		self.xml_root = self.xml_tree.getroot()
Example #40
0
def init_database(connection_url, extra_init=False):
	"""
	Create and initialize the database engine. This must be done before the
	session object can be used. This will also attempt to perform any updates to
	the database schema if the backend supports such operations.

	:param str connection_url: The url for the database connection.
	:param bool extra_init: Run optional extra dbms-specific initialization logic.
	:return: The initialized database engine.
	"""
	connection_url = normalize_connection_url(connection_url)
	connection_url = sqlalchemy.engine.url.make_url(connection_url)
	logger.info("initializing database connection with driver {0}".format(connection_url.drivername))
	if connection_url.drivername == 'sqlite':
		engine = sqlalchemy.create_engine(connection_url, connect_args={'check_same_thread': False}, poolclass=sqlalchemy.pool.StaticPool)
		sqlalchemy.event.listens_for(engine, 'begin')(lambda conn: conn.execute('BEGIN'))
	elif connection_url.drivername == 'postgresql':
		if extra_init:
			init_database_postgresql(connection_url)
		engine = sqlalchemy.create_engine(connection_url)
	else:
		raise errors.KingPhisherDatabaseError('only sqlite and postgresql database drivers are supported')

	Session.remove()
	Session.configure(bind=engine)
	inspector = sqlalchemy.inspect(engine)
	if not 'meta_data' in inspector.get_table_names():
		logger.debug('meta_data table not found, creating all new tables')
		try:
			models.Base.metadata.create_all(engine)
		except sqlalchemy.exc.SQLAlchemyError as error:
			error_lines = (line.strip() for line in error.message.split('\n'))
			raise errors.KingPhisherDatabaseError('SQLAlchemyError: ' + ' '.join(error_lines).strip())

	session = Session()
	set_meta_data('database_driver', connection_url.drivername, session=session)
	schema_version = (get_meta_data('schema_version', session=session) or models.SCHEMA_VERSION)
	session.commit()
	session.close()

	logger.debug("current database schema version: {0} ({1})".format(schema_version, ('latest' if schema_version == models.SCHEMA_VERSION else 'obsolete')))
	if not 'alembic_version' in inspector.get_table_names():
		logger.debug('alembic version table not found, attempting to create and set version')
		init_alembic(engine, schema_version)
	if schema_version > models.SCHEMA_VERSION:
		raise errors.KingPhisherDatabaseError('the database schema is for a newer version, automatic downgrades are not supported')
	elif schema_version < models.SCHEMA_VERSION:
		alembic_config_file = find.data_file('alembic.ini')
		if not alembic_config_file:
			raise errors.KingPhisherDatabaseError('cannot find the alembic.ini configuration file')
		alembic_directory = find.data_directory('alembic')
		if not alembic_directory:
			raise errors.KingPhisherDatabaseError('cannot find the alembic data directory')

		config = alembic.config.Config(alembic_config_file)
		config.config_file_name = alembic_config_file
		config.set_main_option('script_location', alembic_directory)
		config.set_main_option('skip_logger_config', 'True')
		config.set_main_option('sqlalchemy.url', str(connection_url))

		logger.warning("automatically updating the database schema from version {0} to {1}".format(schema_version, models.SCHEMA_VERSION))
		try:
			alembic.command.upgrade(config, 'head')
		except Exception as error:
			logger.critical("database schema upgrade failed with exception: {0}.{1} {2}".format(error.__class__.__module__, error.__class__.__name__, getattr(error, 'message', '')).rstrip(), exc_info=True)
			raise errors.KingPhisherDatabaseError('failed to upgrade to the latest database schema')
		logger.info("successfully updated the database schema from version {0} to {1}".format(schema_version, models.SCHEMA_VERSION))
		# reset it because it may have been altered by alembic
		Session.remove()
		Session.configure(bind=engine)
		session = Session()
	set_meta_data('schema_version', models.SCHEMA_VERSION)

	logger.debug("connected to {0} database: {1}".format(connection_url.drivername, connection_url.database))
	signals.db_initialized.send(connection_url)
	return engine
Example #41
0
	def setUp(self):
		self.config = configuration.Configuration.from_file(find.data_file('server_config.yml'))
Example #42
0
	def test_find_data_file(self):
		self.assertIsNotNone(find.data_file('security.json'))
Example #43
0
	def setUp(self):
		self.config = configuration.Configuration.from_file(find.data_file('server_config.yml'))
		self.tmp_directory = tempfile.mkdtemp()