Beispiel #1
0
    def page(self) -> None:
        assert user.id is not None

        html.set_render_headfoot(False)
        html.add_body_css_class("login")
        html.add_body_css_class("two_factor")
        html.header(_("Two-factor authentication"), Breadcrumb(), javascripts=[])

        html.open_div(id_="login")

        html.open_div(id_="login_window")

        html.open_a(href="https://checkmk.com")
        html.img(
            src=theme.detect_icon_path(icon_name="logo", prefix="mk-"),
            id_="logo",
            class_="custom" if theme.has_custom_logo() else None,
        )
        html.close_a()

        if not is_two_factor_login_enabled(user.id):
            raise MKGeneralException(_("Two-factor authentication not enabled"))

        html.begin_form(
            "two_factor_login", method="POST", add_transid=False, action="user_login_two_factor.py"
        )
        html.prevent_password_auto_completion()
        html.hidden_field(
            "_origtarget", origtarget := request.get_url_input("_origtarget", "index.py")
        )

        if backup_code := request.get_ascii_input("_backup_code"):
            if is_two_factor_backup_code_valid(user.id, backup_code):
                set_two_factor_completed()
                raise HTTPRedirect(origtarget)
Beispiel #2
0
def query_limit_exceeded_warn(limit: Optional[int],
                              user_config: LoggedInUser) -> None:
    """Compare query reply against limits, warn in the GUI about incompleteness"""
    text = HTML(_("Your query produced more than %d results. ") % limit)

    if request.get_ascii_input(
            "limit",
            "soft") == "soft" and user_config.may("general.ignore_soft_limit"):
        text += html.render_a(
            _("Repeat query and allow more results."),
            target="_self",
            href=makeuri(request, [("limit", "hard")]),
        )
    elif request.get_ascii_input("limit") == "hard" and user_config.may(
            "general.ignore_hard_limit"):
        text += html.render_a(
            _("Repeat query without limit."),
            target="_self",
            href=makeuri(request, [("limit", "none")]),
        )

    text += escaping.escape_to_html_permissive(" " + _(
        "<b>Note:</b> the shown results are incomplete and do not reflect the sort order."
    ))
    html.show_warning(text)
Beispiel #3
0
    def _from_vars(self):
        ident = request.get_ascii_input("ident")
        if ident is not None:
            try:
                entry = self._store.filter_editable_entries(self._store.load_for_reading())[ident]
            except KeyError:
                raise MKUserError("ident",
                                  _("This %s does not exist.") % self._mode_type.name_singular())

            self._new = False
            self._ident: Optional[str] = ident
            self._entry = entry
            return

        clone = request.get_ascii_input("clone")
        if clone is not None:
            try:
                entry = self._store.filter_editable_entries(self._store.load_for_reading())[clone]
            except KeyError:
                raise MKUserError("clone",
                                  _("This %s does not exist.") % self._mode_type.name_singular())

            self._new = True
            self._ident = None
            self._entry = copy.deepcopy(entry)
            return

        self._new = True
        self._ident = None
        self._entry = {}
Beispiel #4
0
    def _action(self) -> None:
        assert user.id is not None
        credentials = load_two_factor_credentials(user.id)

        if credential_id := request.get_ascii_input("_delete"):
            if credential_id not in credentials["webauthn_credentials"]:
                return
            del credentials["webauthn_credentials"][credential_id]
            save_two_factor_credentials(user.id, credentials)
            flash(_("Credential has been deleted"))
Beispiel #5
0
 def get_request(self) -> SiteRequest:
     ascii_input = request.get_ascii_input("request")
     if ascii_input is None:
         raise MKUserError(
             "request",
             _("The parameter \"%s\" is missing.") % "request")
     return SiteRequest.deserialize(ast.literal_eval(ascii_input))
Beispiel #6
0
def inpage_search_form(mode: Optional[str] = None,
                       default_value: str = "") -> None:
    form_name = "inpage_search_form"
    reset_button_id = "%s_reset" % form_name
    was_submitted = request.get_ascii_input("filled_in") == form_name
    html.begin_form(form_name, add_transid=False)
    html.text_input(
        "search",
        size=32,
        default_value=default_value,
        placeholder=_("Filter"),
        required=True,
        title="",
    )
    html.hidden_fields()
    if mode:
        html.hidden_field("mode", mode, add_var=True)
    reset_url = request.get_ascii_input_mandatory(
        "reset_url", requested_file_with_query(request))
    html.hidden_field("reset_url", reset_url, add_var=True)
    html.button("submit", "", cssclass="submit", help_=_("Apply"))
    html.buttonlink(reset_url, "", obj_id=reset_button_id, title=_("Reset"))
    html.end_form()
    html.javascript("cmk.page_menu.inpage_search_init(%s, %s)" %
                    (json.dumps(reset_button_id), json.dumps(was_submitted)))
Beispiel #7
0
    def _from_vars(self) -> None:
        self._name = request.get_ascii_input("edit")  # missing -> new group
        self._new = self._name is None

        if self._new:
            clone_group = request.get_ascii_input("clone")
            if clone_group:
                self._name = clone_group

                self.group = self._get_group(self._name)
            else:
                self.group = {}
        else:
            self.group = self._get_group(self._name)

        self.group.setdefault("alias", self._name)
Beispiel #8
0
    def _evaluate_user_opts(self) -> Tuple[TableRows, bool, Optional[str]]:
        assert self.id is not None
        table_id = ensure_str(self.id)
        rows = self.rows

        search_term = None
        actions_enabled = (self.options["searchable"]
                           or self.options["sortable"])

        if not actions_enabled:
            return rows, False, None

        table_opts = user.tableoptions.setdefault(table_id, {})

        # Handle the initial visibility of the actions
        actions_visible = table_opts.get('actions_visible', False)
        if request.get_ascii_input('_%s_actions' % table_id):
            actions_visible = request.get_ascii_input('_%s_actions' %
                                                      table_id) == '1'
            table_opts['actions_visible'] = actions_visible

        if self.options["searchable"]:
            search_term = request.get_unicode_input_mandatory('search', '')
            # Search is always lower case -> case insensitive
            search_term = search_term.lower()
            if search_term:
                request.set_var('search', search_term)
                rows = _filter_rows(rows, search_term)

        if request.get_ascii_input('_%s_reset_sorting' % table_id):
            request.del_var('_%s_sort' % table_id)
            if 'sort' in table_opts:
                del table_opts['sort']  # persist

        if self.options["sortable"]:
            # Now apply eventual sorting settings
            sort = self._get_sort_column(table_opts)
            if sort is not None:
                request.set_var('_%s_sort' % table_id, sort)
                table_opts['sort'] = sort  # persist
                sort_col, sort_reverse = map(int, sort.split(',', 1))
                rows = _sort_rows(rows, sort_col, sort_reverse)

        if actions_enabled:
            user.save_tableoptions()

        return rows, actions_visible, search_term
Beispiel #9
0
    def __init__(
        self,
        table_id: Optional[str] = None,
        title: Optional["HTMLContent"] = None,
        searchable: bool = True,
        sortable: bool = True,
        foldable: Foldable = Foldable.NOT_FOLDABLE,
        limit: Union[None, int, Literal[False]] = None,
        output_format: str = "html",
        omit_if_empty: bool = False,
        omit_empty_columns: bool = False,
        omit_headers: bool = False,
        omit_update_header: bool = False,
        empty_text: Optional[str] = None,
        help: Optional[str] = None,  # pylint: disable=redefined-builtin
        css: Optional[str] = None,
        isopen: bool = True,
    ):
        super().__init__()
        self.next_func = lambda: None
        self.next_header: Optional[str] = None

        # Use our pagename as table id if none is specified
        table_id = table_id if table_id is not None else requested_file_name(
            request)
        assert table_id is not None

        # determine row limit
        if limit is None:
            limit = config.table_row_limit
        if request.get_ascii_input(
                "limit") == "none" or output_format != "html":
            limit = None

        self.id = table_id
        self.title = title
        self.rows: TableRows = []
        self.limit = limit
        self.limit_reached = False
        self.limit_hint: Optional[int] = None
        self.headers: List[TableHeader] = []
        self.options = {
            "collect_headers": False,  # also: True, "finished"
            "omit_if_empty": omit_if_empty,
            "omit_empty_columns": omit_empty_columns,
            "omit_headers": omit_headers,
            "omit_update_header": omit_update_header,
            "searchable": searchable,
            "sortable": sortable,
            "foldable": foldable,
            "output_format": output_format,  # possible: html, csv, fetch
        }

        self.empty_text = empty_text if empty_text is not None else _(
            "No entries.")
        self.help = help
        self.css = css
        self.mode = "row"
        self.isopen: Final = isopen
Beispiel #10
0
def edit_dictionaries(dictionaries: 'Sequence[Tuple[str, Union[Transform, Dictionary]]]',
                      value: Dict[str, Any],
                      focus: Optional[str] = None,
                      hover_help: bool = True,
                      validate: Optional[Callable[[Any], None]] = None,
                      title: Optional[str] = None,
                      method: str = "GET",
                      preview: bool = False,
                      varprefix: str = "",
                      formname: str = "form",
                      consume_transid: bool = True):

    if request.get_ascii_input("filled_in") == formname and transactions.transaction_valid():
        if not preview and consume_transid:
            transactions.check_transaction()

        messages: List[str] = []
        new_value: Dict[str, Dict[str, Any]] = {}
        for keyname, vs_dict in dictionaries:
            dict_varprefix = varprefix + keyname
            new_value[keyname] = {}
            try:
                edited_value = vs_dict.from_html_vars(dict_varprefix)
                vs_dict.validate_value(edited_value, dict_varprefix)
                new_value[keyname].update(edited_value)
            except MKUserError as e:
                messages.append("%s: %s" % (vs_dict.title() or _("Properties"), e))
                user_errors.add(e)
            except Exception as e:
                messages.append("%s: %s" % (vs_dict.title() or _("Properties"), e))
                user_errors.add(MKUserError(None, str(e)))

            if validate and not user_errors:
                try:
                    validate(new_value[keyname])
                except MKUserError as e:
                    messages.append(str(e))
                    user_errors.add(e)

        if messages:
            messages_joined = "".join(["%s<br>\n" % m for m in messages])
            if not preview:
                html.show_error(messages_joined)
            else:
                raise MKUserError(None, messages_joined)
        else:
            return new_value

    html.begin_form(formname, method=method)
    for keyname, vs_dict in dictionaries:
        dict_varprefix = varprefix + keyname
        subvalue = value.get(keyname, {})
        vs_dict.render_input_as_form(dict_varprefix, subvalue)

    end()
    # Should be ignored be hidden_fields, but I do not dare to change it there
    request.del_var("filled_in")
    html.hidden_fields()
    html.end_form()
Beispiel #11
0
    def _from_vars(self) -> None:
        edit_group = request.get_ascii_input("edit")  # missing -> new group
        self._name = GroupName(edit_group) if edit_group else None
        self._new = self._name is None

        if self._new:
            clone_group = request.get_ascii_input("clone")
            if clone_group:
                self._name = GroupName(clone_group)
                self.group = self._get_group(self._name)
            else:
                self.group = {}
        else:
            assert self._name is not None
            self.group = self._get_group(self._name)

        self.group.setdefault("alias", self._name)
Beispiel #12
0
    def get_request(self) -> FetchAgentOutputRequest:
        user.need_permission("wato.download_agent_output")

        ascii_input = request.get_ascii_input("request")
        if ascii_input is None:
            raise MKUserError("request",
                              _('The parameter "%s" is missing.') % "request")
        return FetchAgentOutputRequest.deserialize(
            ast.literal_eval(ascii_input))
Beispiel #13
0
 def _init_host(self) -> watolib.CREHost:
     clonename = request.get_ascii_input("clone")
     if not clonename:
         return self._init_new_host_object()
     if not watolib.Folder.current().has_host(clonename):
         raise MKUserError("host", _("You called this page with an invalid host name."))
     if not user.may("wato.clone_hosts"):
         raise MKAuthException(_("Sorry, you are not allowed to clone hosts."))
     host = watolib.Folder.current().load_host(clonename)
     self._verify_host_type(host)
     return host
Beispiel #14
0
    def _breadcrumb_item(self) -> BreadcrumbItem:
        """Return the breadcrumb item for the current mode"""
        # For the currently active mode use the same link as the "page title click"
        if request.get_ascii_input("mode") == self.name():
            breadcrumb_url = "javascript:window.location.reload(false)"
        else:
            breadcrumb_url = self._breadcrumb_url()

        return BreadcrumbItem(
            title=self.title(),
            url=breadcrumb_url,
        )
Beispiel #15
0
def _localize_request() -> None:
    previous_language = cmk.gui.i18n.get_current_language()
    user_language = request.get_ascii_input("lang", user.language)

    set_language_cookie(request, response, user_language)
    cmk.gui.i18n.localize(user_language)

    # All plugins might have to be reloaded due to a language change. Only trigger
    # a second plugin loading when the user is really using a custom localized GUI.
    # Otherwise the load_all_plugins() at the beginning of the request is sufficient.
    if cmk.gui.i18n.get_current_language() != previous_language:
        load_all_plugins()
Beispiel #16
0
    def _show_form(self) -> None:
        assert user.id is not None

        users = userdb.load_users()

        change_reason = request.get_ascii_input("reason")

        if change_reason == "expired":
            html.p(
                _("Your password is too old, you need to choose a new password."
                  ))
        elif change_reason == "enforced":
            html.p(
                _("You are required to change your password before proceeding."
                  ))

        user_spec = users.get(user.id)
        if user_spec is None:
            html.show_warning(_("Sorry, your user account does not exist."))
            html.footer()
            return

        locked_attributes = userdb.locked_attributes(
            user_spec.get("connector"))
        if "password" in locked_attributes:
            raise MKUserError(
                "cur_password",
                _("You can not change your password, because it is "
                  "managed by another system."),
            )

        html.begin_form("profile", method="POST")
        html.prevent_password_auto_completion()
        html.open_div(class_="wato")
        forms.header(self._page_title())

        forms.section(_("Current Password"))
        html.password_input("cur_password", autocomplete="new-password")

        forms.section(_("New Password"))
        html.password_input("password", autocomplete="new-password")

        forms.section(_("New Password Confirmation"))
        html.password_input("password2", autocomplete="new-password")

        forms.end()
        html.close_div()
        html.hidden_fields()
        html.end_form()
        html.footer()
Beispiel #17
0
    def _from_vars(self):
        self._connection_id = request.get_ascii_input("id")
        self._connection_cfg = {}
        self._connections = load_connection_config(lock=transactions.is_transaction())

        if self._connection_id is None:
            clone_id = request.var("clone")
            if clone_id is not None:
                self._connection_cfg = self._get_connection_cfg_and_index(clone_id)[0]

            self._new = True
            return

        self._new = False
        self._connection_cfg, self._connection_nr = self._get_connection_cfg_and_index(
            self._connection_id
        )
Beispiel #18
0
    def action(self) -> ActionResult:
        if not transactions.transaction_valid():
            return None

        action_var = request.get_str_input("_action")
        if action_var is None:
            return None

        if action_var != "delete":
            return self._handle_custom_action(action_var)

        if not transactions.check_transaction():
            return redirect(mode_url(self._mode_type.list_mode_name()))

        entries = self._store.load_for_modification()

        ident = request.get_ascii_input("_delete")
        if ident not in entries:
            raise MKUserError(
                "_delete",
                _("This %s does not exist.") % self._mode_type.name_singular())

        if ident not in self._store.filter_editable_entries(entries):
            raise MKUserError(
                "_delete",
                _("You are not allowed to delete this %s.") %
                self._mode_type.name_singular(),
            )

        self._validate_deletion(ident, entries[ident])

        entry = entries.pop(ident)
        self._add_change(
            action="delete",
            text=_("Removed the %s '%s'") %
            (self._mode_type.name_singular(), ident),
            affected_sites=self._mode_type.affected_sites(entry),
        )
        self._store.save(entries)

        flash(_("The %s has been deleted.") % self._mode_type.name_singular())
        return redirect(mode_url(self._mode_type.list_mode_name()))
Beispiel #19
0
    def _from_vars(self):
        self._name = request.get_ascii_input(
            "edit")  # missing -> new custom attr
        self._new = self._name is None

        # TODO: Inappropriate Intimacy: custom host attributes should not now about
        #       custom user attributes and vice versa. The only reason they now about
        #       each other now is that they are stored in one file.
        self._all_attrs = load_custom_attrs_from_mk_file(
            lock=transactions.is_transaction())

        if not self._new:
            matching_attrs = [
                a for a in self._attrs if a["name"] == self._name
            ]
            if not matching_attrs:
                raise MKUserError(None, _("The attribute does not exist."))
            self._attr: Dict[str, Any] = matching_attrs[0]
        else:
            self._attr = {}
Beispiel #20
0
 def _from_vars(self) -> None:
     self._checkmk_config_files_map = get_checkmk_config_files_map()
     self._checkmk_log_files_map = get_checkmk_log_files_map()
     self._collect_dump = bool(request.get_ascii_input("_collect_dump"))
     self._diagnostics_parameters = self._get_diagnostics_parameters()
     self._job = DiagnosticsDumpBackgroundJob()
Beispiel #21
0
    def _render_headers(self, actions_enabled: bool, actions_visible: bool,
                        empty_columns: List[bool]) -> None:
        if self.options["omit_headers"]:
            return

        table_id = self.id

        html.open_tr()
        first_col = True
        for nr, header in enumerate(self.headers):
            if self.options["omit_empty_columns"] and empty_columns[nr]:
                continue

            if header.help_txt:
                header_title: HTML = html.render_span(header.title,
                                                      title=header.help_txt)
            else:
                header_title = header.title

            if not isinstance(header.css, list):
                css_class: "CSSSpec" = [header.css]
            else:
                css_class = header.css

            assert isinstance(css_class, list)
            css_class = [("header_%s" % c) for c in css_class if c is not None]

            if not self.options["sortable"] or not header.sortable:
                html.open_th(class_=css_class)
            else:
                css_class.insert(0, "sort")
                reverse = 0
                sort = request.get_ascii_input("_%s_sort" % table_id)
                if sort:
                    sort_col, sort_reverse = map(int, sort.split(",", 1))
                    if sort_col == nr:
                        reverse = 1 if sort_reverse == 0 else 0

                action_uri = makeactionuri(request, transactions,
                                           [("_%s_sort" % table_id, "%d,%d" %
                                             (nr, reverse))])
                html.open_th(
                    class_=css_class,
                    title=_("Sort by %s") % header.title,
                    onclick="location.href='%s'" % action_uri,
                )

            # Add the table action link
            if first_col:
                first_col = False
                if actions_enabled:
                    if not header_title:
                        header_title = HTML(
                            "&nbsp;"
                        )  # Fixes layout problem with white triangle

                    if actions_visible:
                        state = "0"
                        help_txt = _("Hide table actions")
                        img = "table_actions_on"
                    else:
                        state = "1"
                        help_txt = _("Display table actions")
                        img = "table_actions_off"

                    html.open_div(class_=["toggle_actions"])
                    html.icon_button(
                        makeuri(request, [("_%s_actions" % table_id, state)]),
                        help_txt,
                        img,
                        cssclass="toggle_actions",
                    )
                    html.span(header_title)
                    html.close_div()
                else:
                    html.write_text(header_title)
            else:
                html.write_text(header_title)

            html.close_th()
        html.close_tr()
Beispiel #22
0
 def _get_sort_column(self, table_opts: Dict[str, Any]) -> Optional[str]:
     return request.get_ascii_input("_%s_sort" % self.id,
                                    table_opts.get("sort"))
Beispiel #23
0
def _localize_request() -> None:
    user_language = request.get_ascii_input("lang", user.language)
    set_language_cookie(request, response, user_language)
    cmk.gui.i18n.localize(user_language)