Blame org_fedora_oscap/gui/spokes/oscap.py

Packit 792a06
#
Packit 792a06
# Copyright (C) 2013  Red Hat, Inc.
Packit 792a06
#
Packit 792a06
# This copyrighted material is made available to anyone wishing to use,
Packit 792a06
# modify, copy, or redistribute it subject to the terms and conditions of
Packit 792a06
# the GNU General Public License v.2, or (at your option) any later version.
Packit 792a06
# This program is distributed in the hope that it will be useful, but WITHOUT
Packit 792a06
# ANY WARRANTY expressed or implied, including the implied warranties of
Packit 792a06
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Packit 792a06
# Public License for more details.  You should have received a copy of the
Packit 792a06
# GNU General Public License along with this program; if not, write to the
Packit 792a06
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit 792a06
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
Packit 792a06
# source code or documentation are not subject to the GNU General Public
Packit 792a06
# License and may only be used or replicated with the express permission of
Packit 792a06
# Red Hat, Inc.
Packit 792a06
#
Packit 792a06
# Red Hat Author(s): Vratislav Podzimek <vpodzime@redhat.com>
Packit 792a06
#
Packit 792a06
Packit 792a06
import threading
Packit 792a06
import logging
Packit 792a06
from functools import wraps
Packit 792a06
Packit 792a06
# the path to addons is in sys.path so we can import things
Packit 792a06
# from org_fedora_oscap
Packit 792a06
from org_fedora_oscap import common
Packit 792a06
from org_fedora_oscap import data_fetch
Packit 792a06
from org_fedora_oscap import rule_handling
Packit 792a06
from org_fedora_oscap import content_handling
Packit 792a06
from org_fedora_oscap import utils
Packit 792a06
from org_fedora_oscap.common import dry_run_skip
Packit 792a06
from pyanaconda.threading import threadMgr, AnacondaThread
Packit 792a06
from pyanaconda.ui.gui.spokes import NormalSpoke
Packit 792a06
from pyanaconda.ui.communication import hubQ
Packit 792a06
from pyanaconda.ui.gui.utils import async_action_wait, really_hide, really_show
Packit 792a06
from pyanaconda.ui.gui.utils import set_treeview_selection, fire_gtk_action
Packit 792a06
from pyanaconda.ui.categories.system import SystemCategory
Packit 792a06
from pykickstart.errors import KickstartValueError
Packit 792a06
Packit 792a06
from pyanaconda.modules.common.constants.services import USERS
Packit 792a06
Packit 792a06
# pylint: disable-msg=E0611
Packit 792a06
from gi.repository import Gdk
Packit 792a06
Packit 792a06
log = logging.getLogger("anaconda")
Packit 792a06
Packit 792a06
_ = common._
Packit 792a06
N_ = common.N_
Packit 792a06
Packit 792a06
# export only the spoke, no helper functions, classes or constants
Packit 792a06
__all__ = ["OSCAPSpoke"]
Packit 792a06
Packit 792a06
# pages in the main notebook
Packit 792a06
SET_PARAMS_PAGE = 0
Packit 792a06
GET_CONTENT_PAGE = 1
Packit 792a06
Packit 792a06
Packit 792a06
class GtkActionList(object):
Packit 792a06
    """Class for scheduling Gtk actions to be all run at once."""
Packit 792a06
Packit 792a06
    def __init__(self):
Packit 792a06
        self._actions = []
Packit 792a06
Packit 792a06
    def add_action(self, func, *args):
Packit 792a06
        """Add Gtk action to be run later."""
Packit 792a06
Packit 792a06
        @async_action_wait
Packit 792a06
        def gtk_action():
Packit 792a06
            func(*args)
Packit 792a06
Packit 792a06
        self._actions.append(gtk_action)
Packit 792a06
Packit 792a06
    def fire(self):
Packit 792a06
        """Run all scheduled Gtk actions."""
Packit 792a06
Packit 792a06
        for action in self._actions:
Packit 792a06
            action()
Packit 792a06
Packit 792a06
        self._actions = []
Packit 792a06
Packit 792a06
Packit 792a06
# helper functions
Packit 792a06
def set_combo_selection(combo, item, unset_first=False):
Packit 792a06
    """
Packit 792a06
    Set selected item of the combobox.
Packit 792a06
Packit 792a06
    :return: True if successfully set, False otherwise
Packit 792a06
    :rtype: bool
Packit 792a06
Packit 792a06
    """
Packit 792a06
Packit 792a06
    if unset_first:
Packit 792a06
        combo.set_active_iter(None)
Packit 792a06
Packit 792a06
    model = combo.get_model()
Packit 792a06
    if not model:
Packit 792a06
        return False
Packit 792a06
Packit 792a06
    itr = model.get_iter_first()
Packit 792a06
    while itr:
Packit 792a06
        if model[itr][0] == item:
Packit 792a06
            combo.set_active_iter(itr)
Packit 792a06
            return True
Packit 792a06
Packit 792a06
        itr = model.iter_next(itr)
Packit 792a06
Packit 792a06
        return False
Packit 792a06
Packit 792a06
Packit 792a06
def get_combo_selection(combo):
Packit 792a06
    """
Packit 792a06
    Get the selected item of the combobox.
Packit 792a06
Packit 792a06
    :return: selected item or None
Packit 792a06
Packit 792a06
    """
Packit 792a06
Packit 792a06
    model = combo.get_model()
Packit 792a06
    itr = combo.get_active_iter()
Packit 792a06
    if not itr or not model:
Packit 792a06
        return None
Packit 792a06
Packit 792a06
    return model[itr][0]
Packit 792a06
Packit 792a06
Packit 792a06
def render_message_type(column, renderer, model, itr, user_data=None):
Packit 792a06
    # get message type from the first column
Packit 792a06
    value = model[itr][0]
Packit 792a06
Packit 792a06
    if value == common.MESSAGE_TYPE_FATAL:
Packit 792a06
        renderer.set_property("stock-id", "gtk-dialog-error")
Packit 792a06
    elif value == common.MESSAGE_TYPE_WARNING:
Packit 792a06
        renderer.set_property("stock-id", "gtk-dialog-warning")
Packit 792a06
    elif value == common.MESSAGE_TYPE_INFO:
Packit 792a06
        renderer.set_property("stock-id", "gtk-info")
Packit 792a06
    else:
Packit 792a06
        renderer.set_property("stock-id", "gtk-dialog-question")
Packit 792a06
Packit 792a06
Packit 792a06
def set_ready(func):
Packit 792a06
    @wraps(func)
Packit 792a06
    def decorated(self, *args, **kwargs):
Packit 792a06
        ret = func(self, *args, **kwargs)
Packit 792a06
Packit 792a06
        self._unitialized_status = None
Packit 792a06
        self._ready = True
Packit 792a06
        # pylint: disable-msg=E1101
Packit 792a06
        hubQ.send_ready(self.__class__.__name__, True)
Packit 792a06
        hubQ.send_message(self.__class__.__name__, self.status)
Packit 792a06
Packit 792a06
        return ret
Packit 792a06
Packit 792a06
    return decorated
Packit 792a06
Packit 792a06
Packit 792a06
class OSCAPSpoke(NormalSpoke):
Packit 792a06
    """
Packit 792a06
    Main class of the OSCAP addon spoke that will appear in the Security
Packit 792a06
    category on the Summary hub. It allows interactive choosing of the data
Packit 792a06
    stream, checklist and profile driving the evaluation and remediation of the
Packit 792a06
    available SCAP content in the installation process.
Packit 792a06
Packit 792a06
    :see: pyanaconda.ui.common.UIObject
Packit 792a06
    :see: pyanaconda.ui.common.Spoke
Packit 792a06
    :see: pyanaconda.ui.gui.GUIObject
Packit 792a06
Packit 792a06
    """
Packit 792a06
Packit 792a06
    # class attributes defined by API #
Packit 792a06
Packit 792a06
    # list all top-level objects from the .glade file that should be exposed
Packit 792a06
    # to the spoke or leave empty to extract everything
Packit 792a06
    builderObjects = ["OSCAPspokeWindow", "profilesStore", "changesStore",
Packit 792a06
                      "dsStore", "xccdfStore", "profilesStore",
Packit 792a06
                      ]
Packit 792a06
Packit 792a06
    # the name of the main window widget
Packit 792a06
    mainWidgetName = "OSCAPspokeWindow"
Packit 792a06
Packit 792a06
    # name of the .glade file in the same directory as this source
Packit 792a06
    uiFile = "oscap.glade"
Packit 792a06
Packit 792a06
    # id of the help content for this spoke
Packit 792a06
    help_id = "SecurityPolicySpoke"
Packit 792a06
Packit 792a06
    # domain of oscap-anaconda-addon translations
Packit 792a06
    translationDomain = "oscap-anaconda-addon"
Packit 792a06
Packit 792a06
    # category this spoke belongs to
Packit 792a06
    category = SystemCategory
Packit 792a06
Packit 792a06
    # spoke icon (will be displayed on the hub)
Packit 792a06
    # preferred are the -symbolic icons as these are used in Anaconda's spokes
Packit 792a06
    icon = "changes-prevent-symbolic"
Packit 792a06
Packit 792a06
    # title of the spoke (will be displayed on the hub)
Packit 792a06
    title = N_("_Security Policy")
Packit Service 2bd51e
    # The string "SECURITY POLICY" in oscap.glade is meant to be uppercase,
Packit Service 2bd51e
    # as it is displayed inside the spoke as the spoke label,
Packit Service 2bd51e
    # and spoke labels are all uppercase by a convention.
Packit 792a06
Packit 792a06
    # methods defined by API and helper methods #
Packit 792a06
    def __init__(self, data, storage, payload):
Packit 792a06
        """
Packit 792a06
        :see: pyanaconda.ui.common.Spoke.__init__
Packit 792a06
        :param data: data object passed to every spoke to load/store data
Packit 792a06
                     from/to it
Packit 792a06
        :type data: pykickstart.base.BaseHandler
Packit 792a06
        :param storage: object storing storage-related information
Packit 792a06
                        (disks, partitioning, bootloader, etc.)
Packit 792a06
        :type storage: blivet.Blivet
Packit 792a06
        :param payload: object storing packaging-related information
Packit 792a06
        :type payload: pyanaconda.packaging.Payload
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        NormalSpoke.__init__(self, data, storage, payload)
Packit 792a06
        self._addon_data = self.data.addons.org_fedora_oscap
Packit 792a06
        # workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1673071
Packit 792a06
        self.title = _(self.title)
Packit 792a06
        self._storage = storage
Packit 792a06
        self._ready = False
Packit 792a06
Packit 792a06
        # the first status provided
Packit 792a06
        self._unitialized_status = _("Not ready")
Packit 792a06
Packit 792a06
        self._content_handler = None
Packit 792a06
        self._content_handling_cls = None
Packit 792a06
        self._ds_checklists = None
Packit 792a06
Packit 792a06
        # used for changing profiles, stored as self._addon_data.rule_data when
Packit 792a06
        # leaving the spoke
Packit 792a06
        self._rule_data = None
Packit 792a06
Packit 792a06
        # used for storing previously set root password if we need to remove it
Packit 792a06
        # due to the chosen policy (so that we can put it back in case of
Packit 792a06
        # revert)
Packit 792a06
        self.__old_root_pw = None
Packit 792a06
Packit 792a06
        # used to check if the profile was changed or not
Packit 792a06
        self._active_profile = None
Packit 792a06
Packit 792a06
        # prevent multiple simultaneous data fetches
Packit 792a06
        self._fetching = False
Packit 792a06
        self._fetch_flag_lock = threading.Lock()
Packit 792a06
Packit 792a06
        self._error = None
Packit 792a06
Packit 792a06
        # wait for all Anaconda spokes to initialiuze
Packit 792a06
        self._anaconda_spokes_initialized = threading.Event()
Packit 792a06
        self.initialization_controller.init_done.connect(self._all_anaconda_spokes_initialized)
Packit 792a06
Packit 792a06
    def _all_anaconda_spokes_initialized(self):
Packit 792a06
        log.debug("OSCAP addon: Anaconda init_done signal triggered")
Packit 792a06
        self._anaconda_spokes_initialized.set()
Packit 792a06
Packit 792a06
    def initialize(self):
Packit 792a06
        """
Packit 792a06
        The initialize method that is called after the instance is created.
Packit 792a06
        The difference between __init__ and this method is that this may take
Packit 792a06
        a long time and thus could be called in a separated thread.
Packit 792a06
Packit 792a06
        :see: pyanaconda.ui.common.UIObject.initialize
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        NormalSpoke.initialize(self)
Packit 792a06
        column = self.builder.get_object("messageTypeColumn")
Packit 792a06
        renderer = self.builder.get_object("messageTypeRenderer")
Packit 792a06
        column.set_cell_data_func(renderer, render_message_type)
Packit 792a06
Packit 792a06
        # the main notebook containing two pages -- for settings parameters and
Packit 792a06
        # for entering content URL
Packit 792a06
        self._main_notebook = self.builder.get_object("mainNotebook")
Packit 792a06
Packit 792a06
        # the store that holds the messages that come from the rules evaluation
Packit 792a06
        self._message_store = self.builder.get_object("changesStore")
Packit 792a06
Packit 792a06
        # stores with data streams, checklists and profiles
Packit 792a06
        self._ds_store = self.builder.get_object("dsStore")
Packit 792a06
        self._xccdf_store = self.builder.get_object("xccdfStore")
Packit 792a06
        self._profiles_store = self.builder.get_object("profilesStore")
Packit 792a06
Packit 792a06
        # comboboxes for data streams and checklists
Packit 792a06
        self._ids_box = self.builder.get_object("idsBox")
Packit 792a06
        self._ds_combo = self.builder.get_object("dsCombo")
Packit 792a06
        self._xccdf_combo = self.builder.get_object("xccdfCombo")
Packit 792a06
Packit 792a06
        # profiles view and selection
Packit 792a06
        self._profiles_view = self.builder.get_object("profilesView")
Packit 792a06
        self._profiles_selection = self.builder.get_object("profilesSelection")
Packit 792a06
        selected_column = self.builder.get_object("selectedColumn")
Packit 792a06
        selected_renderer = self.builder.get_object("selectedRenderer")
Packit 792a06
        selected_column.set_cell_data_func(selected_renderer,
Packit 792a06
                                           self._render_selected)
Packit 792a06
Packit 792a06
        # button for switching profiles
Packit 792a06
        self._choose_button = self.builder.get_object("chooseProfileButton")
Packit 792a06
Packit 792a06
        # toggle switching the dry-run mode
Packit 792a06
        self._dry_run_switch = self.builder.get_object("dryRunSwitch")
Packit 792a06
Packit 792a06
        # control buttons
Packit 792a06
        self._control_buttons = self.builder.get_object("controlButtons")
Packit 792a06
Packit 792a06
        # content URL entering, content fetching, ...
Packit 792a06
        self._no_content_label = self.builder.get_object("noContentLabel")
Packit 792a06
        self._content_url_entry = self.builder.get_object("urlEntry")
Packit 792a06
        self._fetch_button = self.builder.get_object("fetchButton")
Packit 792a06
        self._progress_box = self.builder.get_object("progressBox")
Packit 792a06
        self._progress_spinner = self.builder.get_object("progressSpinner")
Packit 792a06
        self._progress_label = self.builder.get_object("progressLabel")
Packit 792a06
        self._ssg_button = self.builder.get_object("ssgButton")
Packit 792a06
Packit 792a06
        # if no content was specified and SSG is available, use it
Packit 792a06
        if not self._addon_data.content_type and common.ssg_available():
Packit 792a06
            self._addon_data.content_type = "scap-security-guide"
Packit 792a06
            self._addon_data.content_path = common.SSG_DIR + common.SSG_CONTENT
Packit 792a06
Packit 792a06
        if not self._addon_data.content_defined:
Packit 792a06
            # nothing more to be done now, the spoke is ready
Packit 792a06
            self._ready = True
Packit 792a06
Packit 792a06
            # no more being unitialized
Packit 792a06
            self._unitialized_status = None
Packit 792a06
Packit 792a06
            # user is going to enter the content URL
Packit 792a06
            self._content_url_entry.grab_focus()
Packit 792a06
Packit 792a06
            # pylint: disable-msg=E1101
Packit 792a06
            hubQ.send_ready(self.__class__.__name__, True)
Packit 792a06
        else:
Packit 792a06
            # else fetch data
Packit 792a06
            self._fetch_data_and_initialize()
Packit 792a06
Packit 792a06
    def _render_selected(self, column, renderer, model, itr, user_data=None):
Packit 792a06
        if model[itr][2]:
Packit 792a06
            renderer.set_property("stock-id", "gtk-apply")
Packit 792a06
        else:
Packit 792a06
            renderer.set_property("stock-id", None)
Packit 792a06
Packit 792a06
    def _fetch_data_and_initialize(self):
Packit 792a06
        """Fetch data from a specified URL and initialize everything."""
Packit 792a06
Packit 792a06
        with self._fetch_flag_lock:
Packit 792a06
            if self._fetching:
Packit 792a06
                # prevent multiple fetches running simultaneously
Packit 792a06
                return
Packit 792a06
            self._fetching = True
Packit 792a06
Packit 792a06
        thread_name = None
Packit 792a06
        if any(self._addon_data.content_url.startswith(net_prefix)
Packit 792a06
               for net_prefix in data_fetch.NET_URL_PREFIXES):
Packit 792a06
            # need to fetch data over network
Packit 792a06
            try:
Packit 792a06
                thread_name = common.wait_and_fetch_net_data(
Packit 792a06
                                     self._addon_data.content_url,
Packit 792a06
                                     self._addon_data.raw_preinst_content_path,
Packit 792a06
                                     self._addon_data.certificates)
Packit 792a06
            except common.OSCAPaddonNetworkError:
Packit 792a06
                self._network_problem()
Packit 792a06
                with self._fetch_flag_lock:
Packit 792a06
                    self._fetching = False
Packit 792a06
                return
Packit 792a06
            except KickstartValueError:
Packit 792a06
                self._invalid_url()
Packit 792a06
                with self._fetch_flag_lock:
Packit 792a06
                    self._fetching = False
Packit 792a06
                return
Packit 792a06
Packit 792a06
        # pylint: disable-msg=E1101
Packit 792a06
        hubQ.send_message(self.__class__.__name__,
Packit 792a06
                          _("Fetching content data"))
Packit 792a06
        # pylint: disable-msg=E1101
Packit 792a06
        hubQ.send_not_ready(self.__class__.__name__)
Packit 792a06
        threadMgr.add(AnacondaThread(name="OSCAPguiWaitForDataFetchThread",
Packit 792a06
                                     target=self._init_after_data_fetch,
Packit 792a06
                                     args=(thread_name,)))
Packit 792a06
Packit 792a06
    @set_ready
Packit 792a06
    def _init_after_data_fetch(self, wait_for):
Packit 792a06
        """
Packit 792a06
        Waits for data fetching to be finished, extracts it (if needed),
Packit 792a06
        populates the stores and evaluates pre-installation fixes from the
Packit 792a06
        content and marks the spoke as ready in the end.
Packit 792a06
Packit 792a06
        :param wait_for: name of the thread to wait for (if any)
Packit 792a06
        :type wait_for: str or None
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        try:
Packit 792a06
            threadMgr.wait(wait_for)
Packit 792a06
        except data_fetch.DataFetchError:
Packit 792a06
            self._data_fetch_failed()
Packit 792a06
            with self._fetch_flag_lock:
Packit 792a06
                self._fetching = False
Packit 792a06
            return
Packit 792a06
        finally:
Packit 792a06
            # stop the spinner in any case
Packit 792a06
            fire_gtk_action(self._progress_spinner.stop)
Packit 792a06
Packit 792a06
        if self._addon_data.fingerprint:
Packit 792a06
            hash_obj = utils.get_hashing_algorithm(self._addon_data.fingerprint)
Packit 792a06
            digest = utils.get_file_fingerprint(self._addon_data.raw_preinst_content_path,
Packit 792a06
                                                hash_obj)
Packit 792a06
            if digest != self._addon_data.fingerprint:
Packit 792a06
                self._integrity_check_failed()
Packit 792a06
                # fetching done
Packit 792a06
                with self._fetch_flag_lock:
Packit 792a06
                    self._fetching = False
Packit 792a06
                return
Packit 792a06
Packit 792a06
        # RPM is an archive at this phase
Packit 792a06
        if self._addon_data.content_type in ("archive", "rpm"):
Packit 792a06
            # extract the content
Packit 792a06
            try:
Packit 792a06
                fpaths = common.extract_data(self._addon_data.raw_preinst_content_path,
Packit 792a06
                                             common.INSTALLATION_CONTENT_DIR,
Packit 792a06
                                             [self._addon_data.content_path])
Packit 792a06
            except common.ExtractionError as err:
Packit 792a06
                self._extraction_failed(str(err))
Packit 792a06
                # fetching done
Packit 792a06
                with self._fetch_flag_lock:
Packit 792a06
                    self._fetching = False
Packit 792a06
                return
Packit 792a06
Packit 792a06
            # and populate missing fields
Packit 792a06
            self._content_handling_cls, files = content_handling.explore_content_files(fpaths)
Packit 792a06
            files = common.strip_content_dir(files)
Packit 792a06
Packit 792a06
            # pylint: disable-msg=E1103
Packit 792a06
            self._addon_data.content_path = self._addon_data.content_path or files.xccdf
Packit 792a06
            self._addon_data.cpe_path = self._addon_data.cpe_path or files.cpe
Packit 792a06
            self._addon_data.tailoring_path = (self._addon_data.tailoring_path or
Packit 792a06
                                               files.tailoring)
Packit 792a06
        elif self._addon_data.content_type == "datastream":
Packit 792a06
            self._content_handling_cls = content_handling.DataStreamHandler
Packit 792a06
        elif self._addon_data.content_type == "scap-security-guide":
Packit 792a06
            self._content_handling_cls = content_handling.BenchmarkHandler
Packit 792a06
        else:
Packit 792a06
            raise common.OSCAPaddonError("Unsupported content type")
Packit 792a06
Packit 792a06
        try:
Packit 792a06
            self._content_handler = self._content_handling_cls(self._addon_data.preinst_content_path,
Packit 792a06
                                                               self._addon_data.preinst_tailoring_path)
Packit 792a06
        except content_handling.ContentHandlingError:
Packit 792a06
            self._invalid_content()
Packit 792a06
            # fetching done
Packit 792a06
            with self._fetch_flag_lock:
Packit 792a06
                self._fetching = False
Packit 792a06
Packit 792a06
            return
Packit 792a06
Packit 792a06
        if self._using_ds:
Packit 792a06
            # populate the stores from items from the content
Packit 792a06
            self._ds_checklists = self._content_handler.get_data_streams_checklists()
Packit 792a06
            add_ds_ids = GtkActionList()
Packit 792a06
            add_ds_ids.add_action(self._ds_store.clear)
Packit 792a06
            for dstream in self._ds_checklists.keys():
Packit 792a06
                add_ds_ids.add_action(self._add_ds_id, dstream)
Packit 792a06
            add_ds_ids.fire()
Packit 792a06
Packit 792a06
        self._update_ids_visibility()
Packit 792a06
Packit 792a06
        # refresh UI elements
Packit 792a06
        self.refresh()
Packit 792a06
Packit 792a06
        # let all initialization and configuration happen before we evaluate
Packit 792a06
        # the setup
Packit 792a06
        if not self._anaconda_spokes_initialized.is_set():
Packit 792a06
            # only wait (and log the messages) if the event is not set yet
Packit 792a06
            log.debug("OSCAP addon: waiting for all Anaconda spokes to be initialized")
Packit 792a06
            self._anaconda_spokes_initialized.wait()
Packit 792a06
            log.debug("OSCAP addon: all Anaconda spokes have been initialized - continuing")
Packit 792a06
Packit 792a06
        # try to switch to the chosen profile (if any)
Packit 792a06
        selected = self._switch_profile()
Packit 792a06
Packit 792a06
        if self._addon_data.profile_id and not selected:
Packit 792a06
            # profile ID given, but it was impossible to select it -> invalid
Packit 792a06
            # profile ID given
Packit 792a06
            self._invalid_profile_id()
Packit 792a06
            return
Packit 792a06
Packit 792a06
        # initialize the self._addon_data.rule_data
Packit 792a06
        self._addon_data.rule_data = self._rule_data
Packit 792a06
Packit 792a06
        # update the message store with the messages
Packit 792a06
        self._update_message_store()
Packit 792a06
Packit 792a06
        # all initialized, we can now let user set parameters
Packit 792a06
        fire_gtk_action(self._main_notebook.set_current_page, SET_PARAMS_PAGE)
Packit 792a06
Packit 792a06
        # and use control buttons
Packit 792a06
        fire_gtk_action(really_show, self._control_buttons)
Packit 792a06
Packit 792a06
        # fetching done
Packit 792a06
        with self._fetch_flag_lock:
Packit 792a06
            self._fetching = False
Packit 792a06
Packit 792a06
        # no error
Packit 792a06
        self._set_error(None)
Packit 792a06
Packit 792a06
    @property
Packit 792a06
    def _using_ds(self):
Packit 792a06
        return self._content_handling_cls == content_handling.DataStreamHandler
Packit 792a06
Packit 792a06
    @property
Packit 792a06
    def _current_ds_id(self):
Packit 792a06
        return get_combo_selection(self._ds_combo)
Packit 792a06
Packit 792a06
    @property
Packit 792a06
    def _current_xccdf_id(self):
Packit 792a06
        return get_combo_selection(self._xccdf_combo)
Packit 792a06
Packit 792a06
    @property
Packit 792a06
    def _current_profile_id(self):
Packit 792a06
        store, itr = self._profiles_selection.get_selected()
Packit 792a06
        if not store or not itr:
Packit 792a06
            return None
Packit 792a06
        else:
Packit 792a06
            return store[itr][0]
Packit 792a06
Packit 792a06
    def _add_ds_id(self, ds_id):
Packit 792a06
        """
Packit 792a06
        Add data stream ID to the data streams store.
Packit 792a06
Packit 792a06
        :param ds_id: data stream ID
Packit 792a06
        :type ds_id: str
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        self._ds_store.append([ds_id])
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _update_ids_visibility(self):
Packit 792a06
        """
Packit 792a06
        Updates visibility of the combo boxes that are used to select the DS
Packit 792a06
        and XCCDF IDs.
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        if self._using_ds:
Packit 792a06
            # only show the combo boxes if there are multiple data streams or
Packit 792a06
            # multiple xccdfs (IOW if there's something to choose from)
Packit 792a06
            ds_ids = list(self._ds_checklists.keys())
Packit 792a06
            if len(ds_ids) > 1 or len(self._ds_checklists[ds_ids[0]]) > 1:
Packit 792a06
                really_show(self._ids_box)
Packit 792a06
                return
Packit 792a06
Packit 792a06
        # not showing, hide instead
Packit 792a06
        really_hide(self._ids_box)
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _update_xccdfs_store(self):
Packit 792a06
        """
Packit 792a06
        Clears and repopulates the store with XCCDF IDs from the currently
Packit 792a06
        selected data stream.
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        if self._ds_checklists is None:
Packit 792a06
            # not initialized, cannot do anything
Packit 792a06
            return
Packit 792a06
Packit 792a06
        self._xccdf_store.clear()
Packit 792a06
        for xccdf_id in self._ds_checklists[self._current_ds_id]:
Packit 792a06
            self._xccdf_store.append([xccdf_id])
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _update_profiles_store(self):
Packit 792a06
        """
Packit 792a06
        Clears and repopulates the store with profiles from the currently
Packit 792a06
        selected data stream and checklist.
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        if self._content_handler is None:
Packit 792a06
            # not initialized, cannot do anything
Packit 792a06
            return
Packit 792a06
Packit 792a06
        if self._using_ds and self._ds_checklists is None:
Packit 792a06
            # not initialized, cannot do anything
Packit 792a06
            return
Packit 792a06
Packit 792a06
        self._profiles_store.clear()
Packit 792a06
Packit 792a06
        if self._using_ds:
Packit 792a06
            profiles = self._content_handler.get_profiles(self._current_ds_id,
Packit 792a06
                                                          self._current_xccdf_id)
Packit 792a06
        else:
Packit 792a06
            # pylint: disable-msg=E1103
Packit 792a06
            profiles = self._content_handler.profiles
Packit 792a06
Packit 792a06
        for profile in profiles:
Packit 792a06
            profile_markup = '%s\n%s' \
Packit 792a06
                                % (profile.title, profile.description)
Packit 792a06
            self._profiles_store.append([profile.id,
Packit 792a06
                                         profile_markup,
Packit 792a06
                                         profile.id == self._active_profile])
Packit 792a06
Packit 792a06
    def _add_message(self, message):
Packit 792a06
        """
Packit 792a06
        Add message to the store.
Packit 792a06
Packit 792a06
        :param message: message to be added
Packit 792a06
        :type message: org_fedora_oscap.common.RuleMessage
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        self._message_store.append([message.type, message.text])
Packit 792a06
Packit 792a06
    @dry_run_skip
Packit 792a06
    @async_action_wait
Packit 792a06
    def _update_message_store(self, report_only=False):
Packit 792a06
        """
Packit 792a06
        Updates the message store with messages from rule evaluation.
Packit 792a06
Packit 792a06
        :param report_only: wheter to do changes in configuration or just
Packit 792a06
                            report
Packit 792a06
        :type report_only: bool
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        self._message_store.clear()
Packit 792a06
Packit 792a06
        if not self._rule_data:
Packit 792a06
            # RuleData instance not initialized, cannot do anything
Packit 792a06
            return
Packit 792a06
Packit 792a06
        messages = self._rule_data.eval_rules(self.data, self._storage,
Packit 792a06
                                              report_only)
Packit 792a06
        if not messages:
Packit 792a06
            # no messages from the rules, add a message informing about that
Packit 792a06
            if not self._active_profile:
Packit 792a06
                # because of no profile
Packit 792a06
                message = common.RuleMessage(self.__class__,
Packit 792a06
                                             common.MESSAGE_TYPE_INFO,
Packit 792a06
                                             _("No profile selected"))
Packit 792a06
            else:
Packit 792a06
                # because of no pre-inst rules
Packit 792a06
                message = common.RuleMessage(self.__class__,
Packit 792a06
                                             common.MESSAGE_TYPE_INFO,
Packit 792a06
                                             _("No rules for the pre-installation phase"))
Packit 792a06
            self._add_message(message)
Packit 792a06
Packit 792a06
            # nothing more to be done
Packit 792a06
            return
Packit 792a06
Packit 792a06
        self._resolve_rootpw_issues(messages, report_only)
Packit 792a06
        for msg in messages:
Packit 792a06
            self._add_message(msg)
Packit 792a06
Packit 792a06
    def _resolve_rootpw_issues(self, messages, report_only):
Packit 792a06
        """Mitigate root password issues (which are not fatal in GUI)"""
Packit 792a06
        fatal_rootpw_msgs = [
Packit 792a06
            msg for msg in messages
Packit 792a06
            if msg.origin == rule_handling.PasswdRules and msg.type == common.MESSAGE_TYPE_FATAL]
Packit 792a06
Packit 792a06
        if fatal_rootpw_msgs:
Packit 792a06
            for msg in fatal_rootpw_msgs:
Packit 792a06
                # cannot just change the message type because it is a namedtuple
Packit 792a06
                messages.remove(msg)
Packit 792a06
Packit 792a06
                msg = common.RuleMessage(
Packit 792a06
                    self.__class__, common.MESSAGE_TYPE_WARNING, msg.text)
Packit 792a06
                messages.append(msg)
Packit 792a06
Packit 792a06
            if not report_only:
Packit 792a06
                users_proxy = USERS.get_proxy()
Packit 792a06
Packit 792a06
                self.__old_root_pw = users_proxy.RootPassword
Packit 792a06
                self.data.rootpw.password = None
Packit 792a06
                self.__old_root_pw_seen = users_proxy.IsRootpwKickstarted
Packit 792a06
                self.data.rootpw.seen = False
Packit 792a06
Packit 792a06
    def _revert_rootpw_changes(self):
Packit 792a06
        if self.__old_root_pw is not None:
Packit 792a06
            users_proxy = USERS.get_proxy()
Packit 792a06
Packit 792a06
            users_proxy.SetRootPassword(self.__old_root_pw)
Packit 792a06
            self.__old_root_pw = None
Packit 792a06
Packit 792a06
            users_proxy.SetRootpwKickstarted(self.__old_root_pw_seen)
Packit 792a06
            self.__old_root_pw_seen = None
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _unselect_profile(self, profile_id):
Packit 792a06
        """Unselects the given profile."""
Packit 792a06
Packit 792a06
        if not profile_id:
Packit 792a06
            # no profile specified, nothing to do
Packit 792a06
            return
Packit 792a06
Packit 792a06
        itr = self._profiles_store.get_iter_first()
Packit 792a06
        while itr:
Packit 792a06
            if self._profiles_store[itr][0] == profile_id:
Packit 792a06
                self._profiles_store.set_value(itr, 2, False)
Packit 792a06
            itr = self._profiles_store.iter_next(itr)
Packit 792a06
Packit 792a06
        if self._rule_data:
Packit 792a06
            # revert changes and clear rule_data (no longer valid)
Packit 792a06
            self._rule_data.revert_changes(self.data, self._storage)
Packit 792a06
            self._revert_rootpw_changes()
Packit 792a06
            self._rule_data = None
Packit 792a06
Packit 792a06
        self._active_profile = None
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _select_profile(self, profile_id):
Packit 792a06
        """Selects the given profile."""
Packit 792a06
Packit 792a06
        if not profile_id:
Packit 792a06
            # no profile specified, nothing to do
Packit 792a06
            return False
Packit 792a06
Packit 792a06
        if self._using_ds:
Packit 792a06
            ds = self._current_ds_id
Packit 792a06
            xccdf = self._current_xccdf_id
Packit 792a06
Packit 792a06
            if not all((ds, xccdf, profile_id)):
Packit 792a06
                # something is not set -> do nothing
Packit 792a06
                return False
Packit 792a06
        else:
Packit 792a06
            ds = None
Packit 792a06
            xccdf = None
Packit 792a06
Packit 792a06
        # get pre-install fix rules from the content
Packit 792a06
        try:
Packit 792a06
            rules = common.get_fix_rules_pre(profile_id,
Packit 792a06
                                             self._addon_data.preinst_content_path,
Packit 792a06
                                             ds, xccdf,
Packit 792a06
                                             self._addon_data.preinst_tailoring_path)
Packit 792a06
        except common.OSCAPaddonError as exc:
Packit 792a06
            log.error(
Packit 792a06
                "Failed to get rules for the profile '{}': {}"
Packit 792a06
                .format(profile_id, str(exc)))
Packit 792a06
            self._set_error(
Packit 792a06
                "Failed to get rules for the profile '{}'"
Packit 792a06
                .format(profile_id))
Packit 792a06
            return False
Packit 792a06
Packit 792a06
        itr = self._profiles_store.get_iter_first()
Packit 792a06
        while itr:
Packit 792a06
            if self._profiles_store[itr][0] == profile_id:
Packit 792a06
                self._profiles_store.set_value(itr, 2, True)
Packit 792a06
            itr = self._profiles_store.iter_next(itr)
Packit 792a06
Packit 792a06
        # parse and store rules with a clean RuleData instance
Packit 792a06
        self._rule_data = rule_handling.RuleData()
Packit 792a06
        for rule in rules.splitlines():
Packit 792a06
            self._rule_data.new_rule(rule)
Packit 792a06
Packit 792a06
        # remember the active profile
Packit 792a06
        self._active_profile = profile_id
Packit 792a06
Packit 792a06
        return True
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    @dry_run_skip
Packit 792a06
    def _switch_profile(self):
Packit 792a06
        """Switches to a current selected profile.
Packit 792a06
Packit 792a06
        :returns: whether some profile was selected or not
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        self._set_error(None)
Packit 792a06
        profile = self._current_profile_id
Packit 792a06
        if not profile:
Packit 792a06
            return False
Packit 792a06
Packit 792a06
        self._unselect_profile(self._active_profile)
Packit 792a06
        ret = self._select_profile(profile)
Packit 792a06
Packit 792a06
        # update messages according to the newly chosen profile
Packit 792a06
        self._update_message_store()
Packit 792a06
Packit 792a06
        return ret
Packit 792a06
Packit 792a06
    @set_ready
Packit 792a06
    def _set_error(self, msg):
Packit 792a06
        """Set or clear error message"""
Packit 792a06
        if msg:
Packit 792a06
            self._error = msg
Packit 792a06
            self.clear_info()
Packit 792a06
            self.set_error(msg)
Packit 792a06
        else:
Packit 792a06
            self._error = None
Packit 792a06
            self.clear_info()
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _invalid_content(self):
Packit 792a06
        """Callback for informing user about provided content invalidity."""
Packit 792a06
Packit 792a06
        msg = _("Invalid content provided. Enter a different URL, please.")
Packit 792a06
        self._progress_label.set_markup("%s" % msg)
Packit 792a06
        self._wrong_content(msg)
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _invalid_url(self):
Packit 792a06
        """Callback for informing user about provided URL invalidity."""
Packit 792a06
Packit 792a06
        msg = _("Invalid or unsupported content URL, please enter a different one.")
Packit 792a06
        self._progress_label.set_markup("%s" % msg)
Packit 792a06
        self._wrong_content(msg)
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _data_fetch_failed(self):
Packit 792a06
        """Adapts the UI if fetching data from entered URL failed"""
Packit 792a06
Packit 792a06
        msg = _("Failed to fetch content. Enter a different URL, please.")
Packit 792a06
        self._progress_label.set_markup("%s" % msg)
Packit 792a06
        self._wrong_content(msg)
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _network_problem(self):
Packit 792a06
        """Adapts the UI if network error was encountered during data fetch"""
Packit 792a06
Packit 792a06
        msg = _("Network error encountered when fetching data."
Packit 792a06
                " Please check that network is setup and working.")
Packit 792a06
        self._progress_label.set_markup("%s" % msg)
Packit 792a06
        self._wrong_content(msg)
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _integrity_check_failed(self):
Packit 792a06
        """Adapts the UI if integrity check fails"""
Packit 792a06
Packit 792a06
        msg = _("The integrity check of the content failed. Cannot use the content.")
Packit 792a06
        self._progress_label.set_markup("%s" % msg)
Packit 792a06
        self._wrong_content(msg)
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _extraction_failed(self, err_msg):
Packit 792a06
        """Adapts the UI if extracting data from entered URL failed"""
Packit 792a06
Packit 792a06
        msg = _("Failed to extract content (%s). Enter a different URL, "
Packit 792a06
                "please.") % err_msg
Packit 792a06
        self._progress_label.set_markup("%s" % msg)
Packit 792a06
        self._wrong_content(msg)
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _wrong_content(self, msg):
Packit 792a06
        self._addon_data.clear_all()
Packit 792a06
        really_hide(self._progress_spinner)
Packit 792a06
        self._fetch_button.set_sensitive(True)
Packit 792a06
        self._content_url_entry.set_sensitive(True)
Packit 792a06
        self._content_url_entry.grab_focus()
Packit 792a06
        self._content_url_entry.select_region(0, -1)
Packit 792a06
        self._content_handling_cls = None
Packit 792a06
        self._set_error(msg)
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _invalid_profile_id(self):
Packit 792a06
        msg = _("Profile with ID '%s' not defined in the content. Select a different profile, please") % self._addon_data.profile_id
Packit 792a06
        self._set_error(msg)
Packit 792a06
        self._addon_data.profile_id = None
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def _switch_dry_run(self, dry_run):
Packit 792a06
        self._choose_button.set_sensitive(not dry_run)
Packit 792a06
Packit 792a06
        if dry_run:
Packit 792a06
            # no profile can be selected in the dry-run mode
Packit 792a06
            self._unselect_profile(self._active_profile)
Packit 792a06
Packit 792a06
            # no messages in the dry-run mode
Packit 792a06
            self._message_store.clear()
Packit 792a06
            message = common.RuleMessage(self.__class__,
Packit 792a06
                                         common.MESSAGE_TYPE_INFO,
Packit 792a06
                                         _("Not applying security policy"))
Packit 792a06
            self._add_message(message)
Packit 792a06
Packit 792a06
            self._set_error(None)
Packit 792a06
        else:
Packit 792a06
            # mark the active profile as selected
Packit 792a06
            self._select_profile(self._active_profile)
Packit 792a06
            self._update_message_store()
Packit 792a06
Packit 792a06
    @async_action_wait
Packit 792a06
    def refresh(self):
Packit 792a06
        """
Packit 792a06
        The refresh method that is called every time the spoke is displayed.
Packit 792a06
        It should update the UI elements according to the contents of
Packit 792a06
        self.data.
Packit 792a06
Packit 792a06
        :see: pyanaconda.ui.common.UIObject.refresh
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        if not self._addon_data.content_defined:
Packit 792a06
            # hide the control buttons
Packit 792a06
            really_hide(self._control_buttons)
Packit 792a06
Packit 792a06
            # provide SSG if available
Packit 792a06
            if common.ssg_available():
Packit 792a06
                # show the SSG button and tweak the rest of the line
Packit 792a06
                # (the label)
Packit 792a06
                really_show(self._ssg_button)
Packit 792a06
                # TRANSLATORS: the other choice if SCAP Security Guide is also
Packit 792a06
                # available
Packit 792a06
                tip = _(" or enter data stream content or archive URL below:")
Packit 792a06
            else:
Packit 792a06
                # hide the SSG button
Packit 792a06
                really_hide(self._ssg_button)
Packit 792a06
                tip = _("No content found. Please enter data stream content or "
Packit 792a06
                        "archive URL below:")
Packit 792a06
Packit 792a06
            self._no_content_label.set_text(tip)
Packit 792a06
Packit 792a06
            # hide the progress box, no progress now
Packit 792a06
            with self._fetch_flag_lock:
Packit 792a06
                if not self._fetching:
Packit 792a06
                    really_hide(self._progress_box)
Packit 792a06
Packit 792a06
                    self._content_url_entry.set_sensitive(True)
Packit 792a06
                    self._fetch_button.set_sensitive(True)
Packit 792a06
Packit 792a06
                    if not self._content_url_entry.get_text():
Packit 792a06
                        # no text -> no info/warning
Packit 792a06
                        self._progress_label.set_text("")
Packit 792a06
Packit 792a06
            # switch to the page allowing user to enter content URL and fetch
Packit 792a06
            # it
Packit 792a06
            self._main_notebook.set_current_page(GET_CONTENT_PAGE)
Packit 792a06
            self._content_url_entry.grab_focus()
Packit 792a06
Packit 792a06
            # nothing more to do here
Packit 792a06
            return
Packit 792a06
        else:
Packit 792a06
            # show control buttons
Packit 792a06
            really_show(self._control_buttons)
Packit 792a06
Packit 792a06
            self._main_notebook.set_current_page(SET_PARAMS_PAGE)
Packit 792a06
Packit 792a06
        self._active_profile = self._addon_data.profile_id
Packit 792a06
Packit 792a06
        self._update_ids_visibility()
Packit 792a06
Packit 792a06
        if self._using_ds:
Packit 792a06
            if self._addon_data.datastream_id:
Packit 792a06
                set_combo_selection(self._ds_combo,
Packit 792a06
                                    self._addon_data.datastream_id,
Packit 792a06
                                    unset_first=True)
Packit 792a06
            else:
Packit 792a06
                try:
Packit 792a06
                    default_ds = next(iter(self._ds_checklists.keys()))
Packit 792a06
                    set_combo_selection(self._ds_combo, default_ds,
Packit 792a06
                                        unset_first=True)
Packit 792a06
                except StopIteration:
Packit 792a06
                    # no data stream available
Packit 792a06
                    pass
Packit 792a06
Packit 792a06
                if self._addon_data.datastream_id and self._addon_data.xccdf_id:
Packit 792a06
                    set_combo_selection(self._xccdf_combo,
Packit 792a06
                                        self._addon_data.xccdf_id,
Packit 792a06
                                        unset_first=True)
Packit 792a06
        else:
Packit 792a06
            # no combobox changes --> need to update profiles store manually
Packit 792a06
            self._update_profiles_store()
Packit 792a06
Packit 792a06
        if self._addon_data.profile_id:
Packit 792a06
            set_treeview_selection(self._profiles_view,
Packit 792a06
                                   self._addon_data.profile_id)
Packit 792a06
Packit 792a06
        self._rule_data = self._addon_data.rule_data
Packit 792a06
Packit 792a06
        self._update_message_store()
Packit 792a06
Packit 792a06
    def apply(self):
Packit 792a06
        """
Packit 792a06
        The apply method that is called when the spoke is left. It should
Packit 792a06
        update the contents of self.data with values set in the GUI elements.
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        if not self._addon_data.content_defined or not self._active_profile:
Packit 792a06
            # no errors for no content or no profile
Packit 792a06
            self._set_error(None)
Packit 792a06
Packit 792a06
        # store currently selected values to the addon data attributes
Packit 792a06
        if self._using_ds:
Packit 792a06
            self._addon_data.datastream_id = self._current_ds_id
Packit 792a06
            self._addon_data.xccdf_id = self._current_xccdf_id
Packit 792a06
Packit 792a06
        self._addon_data.profile_id = self._active_profile
Packit 792a06
Packit 792a06
        self._addon_data.rule_data = self._rule_data
Packit 792a06
Packit 792a06
        self._addon_data.dry_run = not self._dry_run_switch.get_active()
Packit 792a06
Packit 792a06
    def execute(self):
Packit 792a06
        """
Packit 792a06
        The excecute method that is called when the spoke is left. It is
Packit 792a06
        supposed to do all changes to the runtime environment according to
Packit 792a06
        the values set in the GUI elements.
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        # nothing to do here
Packit 792a06
        pass
Packit 792a06
Packit 792a06
    @property
Packit 792a06
    def ready(self):
Packit 792a06
        """
Packit 792a06
        The ready property that tells whether the spoke is ready (can be
Packit 792a06
        visited) or not.
Packit 792a06
Packit 792a06
        :rtype: bool
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        return self._ready
Packit 792a06
Packit 792a06
    @property
Packit 792a06
    def completed(self):
Packit 792a06
        """
Packit 792a06
        The completed property that tells whether all mandatory items on the
Packit 792a06
        spoke are set, or not. The spoke will be marked on the hub as completed
Packit 792a06
        or uncompleted acording to the returned value.
Packit 792a06
Packit 792a06
        :rtype: bool
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        # no error message in the store
Packit 792a06
        return not self._error and all(row[0] != common.MESSAGE_TYPE_FATAL
Packit 792a06
                                       for row in self._message_store)
Packit 792a06
Packit 792a06
    @property
Packit 792a06
    @async_action_wait
Packit 792a06
    def status(self):
Packit 792a06
        """
Packit 792a06
        The status property that is a brief string describing the state of the
Packit 792a06
        spoke. It should describe whether all values are set and if possible
Packit 792a06
        also the values themselves. The returned value will appear on the hub
Packit 792a06
        below the spoke's title.
Packit 792a06
Packit 792a06
        :rtype: str
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        if self._error:
Packit 792a06
            return _("Error fetching and loading content")
Packit 792a06
Packit 792a06
        if self._unitialized_status:
Packit 792a06
            # not initialized
Packit 792a06
            return self._unitialized_status
Packit 792a06
Packit 792a06
        if not self._addon_data.content_defined:
Packit 792a06
            return _("No content found")
Packit 792a06
Packit 792a06
        if not self._active_profile:
Packit 792a06
            return _("No profile selected")
Packit 792a06
Packit 792a06
        # update message store, something may changed from the last update
Packit 792a06
        self._update_message_store(report_only=True)
Packit 792a06
Packit 792a06
        warning_found = False
Packit 792a06
        for row in self._message_store:
Packit 792a06
            if row[0] == common.MESSAGE_TYPE_FATAL:
Packit 792a06
                return _("Misconfiguration detected")
Packit 792a06
            elif row[0] == common.MESSAGE_TYPE_WARNING:
Packit 792a06
                warning_found = True
Packit 792a06
Packit 792a06
        # TODO: at least the last two status messages need a better wording
Packit 792a06
        if warning_found:
Packit 792a06
            return _("Warnings appeared")
Packit 792a06
Packit 792a06
        return _("Everything okay")
Packit 792a06
Packit 792a06
    def on_ds_combo_changed(self, *args):
Packit 792a06
        """Handler for the datastream ID change."""
Packit 792a06
Packit 792a06
        ds_id = self._current_ds_id
Packit 792a06
        if not ds_id:
Packit 792a06
            return
Packit 792a06
Packit 792a06
        self._update_xccdfs_store()
Packit 792a06
        first_checklist = self._ds_checklists[ds_id][0]
Packit 792a06
Packit 792a06
        set_combo_selection(self._xccdf_combo, first_checklist)
Packit 792a06
Packit 792a06
    def on_xccdf_combo_changed(self, *args):
Packit 792a06
        """Handler for the XCCDF ID change."""
Packit 792a06
Packit 792a06
        # may take a while
Packit 792a06
        self._update_profiles_store()
Packit 792a06
Packit 792a06
    @dry_run_skip
Packit 792a06
    def on_profiles_selection_changed(self, *args):
Packit 792a06
        """Handler for the profile selection change."""
Packit 792a06
Packit 792a06
        cur_profile = self._current_profile_id
Packit 792a06
        if cur_profile:
Packit 792a06
            if cur_profile != self._active_profile:
Packit 792a06
                # new profile selected, make the selection button sensitive
Packit 792a06
                self._choose_button.set_sensitive(True)
Packit 792a06
            else:
Packit 792a06
                # current active profile selected
Packit 792a06
                self._choose_button.set_sensitive(False)
Packit 792a06
Packit 792a06
    @dry_run_skip
Packit 792a06
    def on_profile_clicked(self, widget, event, *args):
Packit 792a06
        """Handler for the profile being clicked on."""
Packit 792a06
Packit 792a06
        # if a profile is double-clicked, we should switch to it
Packit 792a06
        if event.type == Gdk.EventType._2BUTTON_PRESS:
Packit 792a06
            self._switch_profile()
Packit 792a06
Packit 792a06
            # active profile selected
Packit 792a06
            self._choose_button.set_sensitive(False)
Packit 792a06
Packit 792a06
        # let the other actions hooked to the click happen as well
Packit 792a06
        return False
Packit 792a06
Packit 792a06
    def on_profile_chosen(self, *args):
Packit 792a06
        """
Packit 792a06
        Handler for the profile being chosen
Packit 792a06
        (e.g. "Select profile" button hit).
Packit 792a06
Packit 792a06
        """
Packit 792a06
Packit 792a06
        # switch profile
Packit 792a06
        self._switch_profile()
Packit 792a06
Packit 792a06
        # active profile selected
Packit 792a06
        self._choose_button.set_sensitive(False)
Packit 792a06
Packit 792a06
    def on_fetch_button_clicked(self, *args):
Packit 792a06
        """Handler for the Fetch button"""
Packit 792a06
Packit 792a06
        with self._fetch_flag_lock:
Packit 792a06
            if self._fetching:
Packit 792a06
                # some other fetching/pre-processing running, give up
Packit 792a06
                return
Packit 792a06
Packit 792a06
        # prevent user from changing the URL in the meantime
Packit 792a06
        self._content_url_entry.set_sensitive(False)
Packit 792a06
        self._fetch_button.set_sensitive(False)
Packit 792a06
        url = self._content_url_entry.get_text()
Packit 792a06
        really_show(self._progress_box)
Packit 792a06
        really_show(self._progress_spinner)
Packit 792a06
Packit 792a06
        if not data_fetch.can_fetch_from(url):
Packit 792a06
            msg = _("Invalid or unsupported URL")
Packit 792a06
            # cannot start fetching
Packit 792a06
            self._progress_label.set_markup("%s" % msg)
Packit 792a06
            self._wrong_content(msg)
Packit 792a06
            return
Packit 792a06
Packit 792a06
        self._progress_label.set_text(_("Fetching content..."))
Packit 792a06
        self._progress_spinner.start()
Packit 792a06
        self._addon_data.content_url = url
Packit 792a06
        if url.endswith(".rpm"):
Packit 792a06
            self._addon_data.content_type = "rpm"
Packit 792a06
        elif any(url.endswith(arch_type) for arch_type in common.SUPPORTED_ARCHIVES):
Packit 792a06
            self._addon_data.content_type = "archive"
Packit 792a06
        else:
Packit 792a06
            self._addon_data.content_type = "datastream"
Packit 792a06
Packit 792a06
        self._fetch_data_and_initialize()
Packit 792a06
Packit 792a06
    def on_dry_run_toggled(self, switch, *args):
Packit 792a06
        dry_run = not switch.get_active()
Packit 792a06
        self._addon_data.dry_run = dry_run
Packit 792a06
        self._switch_dry_run(dry_run)
Packit 792a06
Packit 792a06
    def on_change_content_clicked(self, *args):
Packit 792a06
        self._unselect_profile(self._active_profile)
Packit 792a06
        self._addon_data.clear_all()
Packit 792a06
        self.refresh()
Packit 792a06
Packit 792a06
    def on_use_ssg_clicked(self, *args):
Packit 792a06
        self._addon_data.clear_all()
Packit 792a06
        self._addon_data.content_type = "scap-security-guide"
Packit 792a06
        self._addon_data.content_path = common.SSG_DIR + common.SSG_CONTENT
Packit 792a06
        self._fetch_data_and_initialize()