Example #1
0
def test_add_user_error(register_builtin_html):
    assert not html.has_user_errors()
    html.add_user_error(None, "asd <script>alert(1)</script> <br> <b>")
    assert html.has_user_errors()

    with html.plugged():
        html.show_user_errors()
        c = html.drain()
    assert c == "<div class=\"error\">asd &lt;script&gt;alert(1)&lt;/script&gt; <br> <b></div>"
Example #2
0
    def _show_form(self, profile_changed: bool) -> None:
        assert config.user.id is not None

        users = userdb.load_users()

        if profile_changed:
            html.reload_sidebar()
            html.show_message(_("Successfully updated user profile."))
            # Ensure theme changes are applied without additional user interaction
            html.immediate_browser_redirect(0.5, makeuri(global_request, []))

        if html.has_user_errors():
            html.show_user_errors()

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

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

        forms.section(_("Name"), simple=True)
        html.write_text(user.get("alias", config.user.id))

        select_language(user)

        # Let the user configure how he wants to be notified
        rulebased_notifications = rulebased_notifications_enabled()
        if (not rulebased_notifications
                and config.user.may('general.edit_notifications')
                and user.get("notifications_enabled")):
            forms.section(_("Notifications"))
            html.help(
                _("Here you can configure how you want to be notified about host and service problems and "
                  "other monitoring events."))
            watolib.get_vs_flexible_notifications().render_input(
                "notification_method", user.get("notification_method"))

        if config.user.may('general.edit_user_attributes'):
            for name, attr in userdb.get_user_attributes():
                if attr.user_editable():
                    vs = attr.valuespec()
                    forms.section(_u(vs.title()))
                    value = user.get(name, vs.default_value())
                    if not attr.permission() or config.user.may(
                            attr.permission()):
                        vs.render_input("ua_" + name, value)
                        html.help(_u(vs.help()))
                    else:
                        html.write(vs.value_to_text(value))

        forms.end()
        html.close_div()
        html.hidden_fields()
        html.end_form()
        html.footer()
Example #3
0
def render_availability_options(what):
    if html.request.var("_reset"):
        config.user.save_file("avoptions", {})
        html.request.del_vars("avo_")
        html.request.del_var("avoptions")

    avoptions = availability.get_default_avoptions()

    # Users of older versions might not have all keys set. The following
    # trick will merge their options with our default options.
    avoptions.update(config.user.load_file("avoptions", {}))

    is_open = False
    html.begin_form("avoptions")
    html.hidden_field("avoptions", "set")
    avoption_entries = availability.get_avoption_entries(what)
    if html.request.var("avoptions") == "set":
        for name, height, _show_in_reporting, vs in avoption_entries:
            try:
                avoptions[name] = vs.from_html_vars("avo_" + name)
                vs.validate_value(avoptions[name], "avo_" + name)
            except MKUserError as e:
                html.add_user_error(e.varname, e)
                is_open = True

    if html.request.var("_unset_logrow_limit") == "1":
        avoptions["logrow_limit"] = 0

    range_vs = None
    for name, height, _show_in_reporting, vs in avoption_entries:
        if name == 'rangespec':
            range_vs = vs

    try:
        range_, range_title = range_vs.compute_range(avoptions["rangespec"])
        avoptions["range"] = range_, range_title
    except MKUserError as e:
        html.add_user_error(e.varname, e)

    if html.has_user_errors():
        html.show_user_errors()

    html.begin_floating_options("avoptions", is_open)
    for name, height, _show_in_reporting, vs in avoption_entries:
        html.render_floating_option(name, height, "avo_", vs,
                                    avoptions.get(name))
    html.end_floating_options(reset_url=html.makeuri(
        [("_reset", "1")],
        remove_prefix="avo_",
        delvars=["apply", "filled_in"],
    ))

    html.hidden_fields()
    html.end_form()

    if html.form_submitted():
        config.user.save_file("avoptions", avoptions)

    return avoptions
Example #4
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 html.request.get_ascii_input("filled_in") == formname and html.transaction_valid():
        if not preview and consume_transid:
            html.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))
                html.add_user_error(e.varname, e)
            except Exception as e:
                messages.append("%s: %s" % (vs_dict.title() or _("Properties"), e))
                html.add_user_error(None, e)

            if validate and not html.has_user_errors():
                try:
                    validate(new_value[keyname])
                except MKUserError as e:
                    messages.append("%s" % e)
                    html.add_user_error(e.varname, 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
    html.request.del_var("filled_in")
    html.hidden_fields()
    html.end_form()
Example #5
0
    def _show_form(self, profile_changed: bool) -> None:
        assert config.user.id is not None

        users = userdb.load_users()

        change_reason = html.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.'
                  ))

        if profile_changed:
            html.show_message(_("Your password has been changed."))
            if change_reason:
                raise HTTPRedirect(
                    html.request.get_str_input_mandatory(
                        '_origtarget', 'index.py'))

        if html.has_user_errors():
            html.show_user_errors()

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

        locked_attributes = userdb.locked_attributes(user.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()
Example #6
0
    def _show_form(self, profile_changed: bool) -> None:
        assert config.user.id is not None

        users = userdb.load_users()

        if profile_changed:
            flash(_("Successfully updated user profile."))
            # Ensure theme changes are applied without additional user interaction
            html.reload_whole_page()

        if html.has_user_errors():
            html.show_user_errors()

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

        html.begin_form("profile", method="POST")
        html.prevent_password_auto_completion()
        html.open_div(class_="wato")
        forms.header(_("Personal settings"))

        forms.section(_("Name"), simple=True)
        html.write_text(user.get("alias", config.user.id))

        select_language(user)

        # Let the user configure how he wants to be notified
        rulebased_notifications = rulebased_notifications_enabled()
        if (not rulebased_notifications
                and config.user.may('general.edit_notifications')
                and user.get("notifications_enabled")):
            forms.section(_("Notifications"))
            html.help(
                _("Here you can configure how you want to be notified about host and service problems and "
                  "other monitoring events."))
            watolib.get_vs_flexible_notifications().render_input(
                "notification_method", user.get("notification_method"))

        if config.user.may('general.edit_user_attributes'):
            custom_user_attr_topics = get_user_attributes_by_topic()
            _show_custom_user_attr(user,
                                   custom_user_attr_topics.get("personal", []))
            forms.header(_("User interface settings"))
            _show_custom_user_attr(
                user, custom_user_attr_topics.get("interface", []))

        forms.end()
        html.close_div()
        html.hidden_fields()
        html.end_form()
        html.footer()
Example #7
0
def edit_valuespec(
        vs,  # type: Dictionary
        value,  # type: Dict[str, Any]
        buttontext=None,  # type: Optional[Text]
        method="GET",  # type: str
        varprefix="",  # type: str
        validate=None,  # type: Optional[Callable[[Dict[str, Any]], None]]
        formname="form",  # type: str
        consume_transid=True,  # type: bool
        focus=None  # type: Optional[str]
):
    # type: (...) -> Optional[Dict[str, Any]]

    if html.request.get_ascii_input(
            "filled_in") == formname and html.transaction_valid():
        if consume_transid:
            html.check_transaction()

        messages = []
        try:
            new_value = vs.from_html_vars(varprefix)
            vs.validate_value(new_value, varprefix)

        except MKUserError as e:
            messages.append("%s: %s" % (vs.title(), e.message))
            html.add_user_error(e.varname, e.message)

        if validate and not html.has_user_errors():
            try:
                validate(new_value)
            except MKUserError as e:
                messages.append(e.message)
                html.add_user_error(e.varname, e.message)

        if messages:
            html.show_error("".join(["%s<br>\n" % m for m in messages]))
        else:
            return new_value

    html.begin_form(formname, method=method)
    html.help(vs.help())
    vs.render_input(varprefix, value)
    if buttontext is None:
        buttontext = _("Save")
    html.button("save", buttontext)
    # Should be ignored be hidden_fields, but I do not dare to change it there
    html.request.del_var("filled_in")
    html.hidden_fields()
    if focus:
        html.set_focus(focus)
    else:
        vs.set_focus(varprefix)
    html.end_form()
    return None
Example #8
0
    def _show_diagnose_output(self):
        if not html.request.var('_try'):
            html.show_message(
                _('You can diagnose the connection to a specific host using this dialog. '
                  'You can either test whether your current configuration is still working '
                  'or investigate in which ways a host can be reached. Simply configure the '
                  'connection options you like to try on the right side of the screen and '
                  'press the "Test" button. The results will be displayed here.'
                  ))
            return

        if html.has_user_errors():
            html.show_user_errors()
            return

        # TODO: Insert any vs_host valuespec validation
        #       These tests can be called with invalid valuespec settings...
        # TODO: Replace hard coded icon paths with dynamic ones to old or new theme
        for ident, title in ModeDiagHost.diag_host_tests():
            html.h3(title)
            html.open_table(class_=["data", "test"])
            html.open_tr(class_=["data", "odd0"])

            html.open_td(class_="icons")
            html.open_div()
            html.icon(title=None, icon="reload", id_="%s_img" % ident)
            html.open_a(href="")
            html.icon(title=_('Retry this test'),
                      icon="reload",
                      cssclass="retry",
                      id_="%s_retry" % ident)
            html.close_a()
            html.close_div()
            html.close_td()

            html.open_td()
            html.div('', class_="log", id="%s_log" % ident)
            html.close_td()

            html.close_tr()
            html.close_table()
            html.javascript(
                'cmk.host_diagnose.start_test(%s, %s, %s)' %
                (json.dumps(ident), json.dumps(self._hostname),
                 json.dumps(html.transaction_manager.fresh_transid())))
Example #9
0
    def _display_audit_log_options(self):
        if display_options.disabled(display_options.C):
            return

        html.begin_form("options", method="GET")

        self._show_audit_log_options_controls()

        html.open_div(class_="side_popup_content")
        if html.has_user_errors():
            html.show_user_errors()

        for name, vs in self._audit_log_options():
            html.render_floating_option(name, "single", "options_", vs, self._options[name])

        html.close_div()

        html.hidden_fields()
        html.end_form()
Example #10
0
    def page(self) -> None:
        watolib.init_wato_datastructures(with_wato_lock=True)

        title = self._page_title()
        breadcrumb = make_simple_page_breadcrumb(mega_menu_registry.menu_user(), title)
        html.header(title, breadcrumb, self._page_menu(breadcrumb))

        if html.request.has_var('_save') and html.check_transaction():
            try:
                self._action()
            except MKUserError as e:
                html.add_user_error(e.varname, e)

        for message in get_flashed_messages():
            html.show_message(message)

        if html.has_user_errors():
            html.show_user_errors()

        self._show_form()
Example #11
0
    def _show_login_page(self) -> None:
        html.set_render_headfoot(False)
        html.add_body_css_class("login")
        html.header(config.get_page_heading(), Breadcrumb(), javascripts=[])

        default_origtarget = ("index.py" if html.myfile in ["login", "logout"]
                              else makeuri(global_request, []))
        origtarget = html.get_url_input("_origtarget", default_origtarget)

        # Never allow the login page to be opened in the iframe. Redirect top page to login page.
        # This will result in a full screen login page.
        html.javascript('''if(top != self) {
    window.top.location.href = location;
}''')

        # When someone calls the login page directly and is already authed redirect to main page
        if html.myfile == 'login' and _check_auth(html.request):
            raise HTTPRedirect(origtarget)

        html.open_div(id_="login")

        html.open_div(id_="login_window")

        html.div("" if "hide_version" in config.login_screen else
                 cmk_version.__version__,
                 id_="version")

        html.begin_form("login",
                        method='POST',
                        add_transid=False,
                        action='login.py')
        html.hidden_field('_login', '1')
        html.hidden_field('_origtarget', origtarget)
        html.label("%s:" % _('Username'),
                   id_="label_user",
                   class_=["legend"],
                   for_="_username")
        html.br()
        html.text_input("_username", id_="input_user")
        html.label("%s:" % _('Password'),
                   id_="label_pass",
                   class_=["legend"],
                   for_="_password")
        html.br()
        html.password_input("_password", id_="input_pass", size=None)

        if html.has_user_errors():
            html.open_div(id_="login_error")
            html.show_user_errors()
            html.close_div()

        html.open_div(id_="button_text")
        html.button("_login", _('Login'))
        html.close_div()
        html.close_div()

        html.open_div(id_="foot")

        if config.login_screen.get("login_message"):
            html.open_div(id_="login_message")
            html.show_message(config.login_screen["login_message"])
            html.close_div()

        footer: List[Union[HTML, str]] = []
        for title, url, target in config.login_screen.get("footer_links", []):
            footer.append(html.render_a(title, href=url, target=target))

        if "hide_version" not in config.login_screen:
            footer.append("Version: %s" % cmk_version.__version__)

        footer.append("&copy; %s" % html.render_a(
            "tribe29 GmbH", href="https://checkmk.com", target="_blank"))

        html.write(HTML(" - ").join(footer))

        if cmk_version.is_raw_edition():
            html.br()
            html.br()
            html.write(
                _('You can use, modify and distribute Check_MK under the terms of the <a href="%s" target="_blank">'
                  'GNU GPL Version 2</a>.') % "https://checkmk.com/gpl.html")

        html.close_div()

        html.set_focus('_username')
        html.hidden_fields()
        html.end_form()
        html.close_div()

        html.footer()
Example #12
0
def render_bi_availability(title, aggr_rows):
    # type: (Text, Rows) -> None
    config.user.need_permission("general.see_availability")

    av_mode = html.request.get_ascii_input_mandatory("av_mode", "availability")
    avoptions = get_availability_options_from_url("bi")
    if av_mode == "timeline":
        title = _("Timeline of") + " " + title
    else:
        title = _("Availability of") + " " + title

    if html.output_format != "csv_export":
        html.body_start(title)
        html.top_heading(title)
        html.begin_context_buttons()
        html.toggle_button("avoptions", False, "painteroptions",
                           _("Configure details of the report"))
        html.context_button(_("Status View"),
                            html.makeuri([("mode", "status")]), "status")
        if config.reporting_available() and config.user.may(
                "general.reporting"):
            html.context_button(_("Export as PDF"),
                                html.makeuri([], filename="report_instant.py"),
                                "report")
        if config.user.may("general.csv_export"):
            html.context_button(
                _("Export as CSV"),
                html.makeuri([("output_format", "csv_export")]),
                "download_csv")

        if av_mode == "timeline":
            html.context_button(_("Availability"),
                                html.makeuri([("av_mode", "availability")]),
                                "availability")

        elif len(aggr_rows) == 1:
            aggr_name = aggr_rows[0]["aggr_name"]
            aggr_group = aggr_rows[0]["aggr_group"]
            timeline_url = html.makeuri([("av_mode", "timeline"),
                                         ("av_aggr_name", aggr_name),
                                         ("av_aggr_group", aggr_group)])
            html.context_button(_("Timeline"), timeline_url, "timeline")
        html.end_context_buttons()

        avoptions = render_availability_options("bi")

    if not html.has_user_errors():
        logrow_limit = avoptions["logrow_limit"]
        if logrow_limit == 0:
            livestatus_limit = None
        else:
            livestatus_limit = (len(aggr_rows) * logrow_limit)

        spans = []  # type: List[AVSpan]

        # iterate all aggregation rows
        timewarpcode = HTML()
        timewarp = html.request.get_integer_input("timewarp")

        has_reached_logrow_limit = False
        timeline_containers, fetched_rows = availability.get_timeline_containers(
            aggr_rows, avoptions, timewarp,
            livestatus_limit + 1 if livestatus_limit is not None else None)
        if livestatus_limit and fetched_rows > livestatus_limit:
            has_reached_logrow_limit = True

        for timeline_container in timeline_containers:
            tree = timeline_container.aggr_tree
            these_spans = timeline_container.timeline
            timewarp_tree_state = timeline_container.timewarp_state

            spans += these_spans

            # render selected time warp for the corresponding aggregation row (should be matched by only one)
            if timewarp and timewarp_tree_state:
                state, assumed_state, node, _subtrees = timewarp_tree_state
                eff_state = state
                if assumed_state is not None:
                    eff_state = assumed_state
                row = {
                    "aggr_tree": tree,
                    "aggr_treestate": timewarp_tree_state,
                    "aggr_state": state,  # state disregarding assumptions
                    "aggr_assumed_state":
                    assumed_state,  # is None, if there are no assumptions
                    "aggr_effective_state":
                    eff_state,  # is assumed_state, if there are assumptions, else real state
                    "aggr_name": node["title"],
                    "aggr_output": eff_state["output"],
                    "aggr_hosts": node["reqhosts"],
                    "aggr_function": node["func"],
                    "aggr_group": html.request.var("aggr_group"),
                }

                renderer = bi.FoldableTreeRendererTree(
                    row,
                    omit_root=False,
                    expansion_level=config.user.bi_expansion_level,
                    only_problems=False,
                    lazy=False)
                tdclass, htmlcode = renderer.css_class(), renderer.render()

                with html.plugged():
                    # TODO: SOMETHING IS WRONG IN HERE (used to be the same situation in original code!)
                    # FIXME: WHAT is wrong in here??

                    html.open_h3()
                    # render icons for back and forth
                    button_back_shown = False
                    button_forth_shown = False
                    if int(these_spans[0]["from"]) == timewarp:
                        html.disabled_icon_button("back_off")
                        button_back_shown = True

                    previous_span = None
                    for span in these_spans:
                        if not button_back_shown and int(
                                span["from"]
                        ) == timewarp and previous_span is not None:
                            html.icon_button(
                                html.makeuri([("timewarp",
                                               str(int(previous_span["from"])))
                                              ]), _("Jump one phase back"),
                                "back")
                            button_back_shown = True
                        # Multiple followup spans can have the same "from" time
                        # We only show one forth-arrow with an actual time difference
                        elif not button_forth_shown and previous_span and int(
                                previous_span["from"]) == timewarp and int(
                                    span["from"]) != timewarp:
                            html.icon_button(
                                html.makeuri([("timewarp",
                                               str(int(span["from"])))]),
                                _("Jump one phase forth"), "forth")
                            button_forth_shown = True
                        previous_span = span
                    if not button_forth_shown:
                        html.disabled_icon_button("forth_off")

                    html.write_text(" &nbsp; ")
                    html.icon_button(html.makeuri([("timewarp", "")]),
                                     _("Close Timewarp"), "closetimewarp")
                    html.write_text("%s %s" %
                                    (_("Timewarp to "),
                                     time.strftime("%Y-%m-%d %H:%M:%S",
                                                   time.localtime(timewarp))))
                    html.close_h3()

                    html.open_table(class_=["data", "table", "timewarp"])
                    html.open_tr(class_=["data", "odd0"])
                    html.open_td(class_=tdclass)
                    html.write_html(htmlcode)
                    html.close_td()
                    html.close_tr()
                    html.close_table()

                    timewarpcode += html.drain()

        # Note: 'spans_by_object' returns two arguments which are used by
        # all availability views but not by BI. There we have to take
        # only complete aggregations
        av_rawdata = availability.spans_by_object(spans, None)[0]
        av_data = availability.compute_availability("bi", av_rawdata,
                                                    avoptions)

        # If we abolish the limit we have to fetch the data again
        # with changed logrow_limit = 0, which means no limit
        if has_reached_logrow_limit:
            text = _(
                "Your query matched more than %d log entries. "
                "<b>Note:</b> The shown data does not necessarily reflect the "
                "matched entries and the result might be incomplete. "
            ) % avoptions["logrow_limit"]
            text += html.render_a(_('Repeat query without limit.'),
                                  html.makeuri([("_unset_logrow_limit", "1")]))
            html.show_warning(text)

        if html.output_format == "csv_export" and config.user.may(
                "general.csv_export"):
            _output_csv("bi", av_mode, av_data, avoptions)
            return

        html.write(timewarpcode)
        do_render_availability("bi", av_rawdata, av_data, av_mode, None,
                               avoptions)

    html.bottom_footer()
    html.body_end()
Example #13
0
def render_availability_page(view, filterheaders):
    # type: (View, FilterHeaders) -> None
    config.user.need_permission("general.see_availability")

    if handle_edit_annotations():
        return

    # We make reports about hosts, services or BI aggregates
    if "service" in view.datasource.infos:
        what = "service"
    elif "aggr_name" in view.datasource.infos:
        what = "bi"
    else:
        what = "host"

    avoptions = get_availability_options_from_url(what)
    time_range, range_title = avoptions["range"]

    # We have two display modes:
    # - Show availability table (stats) "table"
    # - Show timeline                   "timeline"
    # --> controlled by URL variable "av_mode"
    av_mode = html.request.get_ascii_input_mandatory("av_mode", "table")

    if av_mode == "timeline":
        title = _("Availability Timeline")
    else:
        title = _("Availability")

    # This is combined with the object selection
    # - Show all objects
    # - Show one specific object
    # --> controlled by URL variables "av_site", "av_host" and "av_service"
    # --> controlled by "av_aggr" in case of BI aggregate
    title += " - "
    av_object = None  # type: AVObjectSpec
    if html.request.var("av_host"):
        av_object = (html.request.get_str_input_mandatory("av_site"),
                     html.request.get_str_input_mandatory("av_host"),
                     html.request.get_unicode_input_mandatory("av_service"))
        title += av_object[1]
        if av_object[2]:
            title += " - " + av_object[2]
    elif html.request.var("av_aggr"):
        av_object = (None, None,
                     html.request.get_unicode_input_mandatory("av_aggr"))
        title += av_object[2]
    else:
        title += view_title(view.spec)

    # Deletion must take place before computation, since it affects the outcome
    with html.plugged():
        handle_delete_annotations()
        confirmation_html_code = html.drain()

    # Now compute all data, we need this also for CSV export
    if not html.has_user_errors():
        include_long_output = av_mode == "timeline" \
                and "timeline_long_output" in avoptions["labelling"]
        av_rawdata, has_reached_logrow_limit = availability.get_availability_rawdata(
            what,
            view.context,
            filterheaders,
            view.only_sites,
            av_object=av_object,
            include_output=av_mode == "timeline",
            include_long_output=include_long_output,
            avoptions=avoptions)
        av_data = availability.compute_availability(what, av_rawdata,
                                                    avoptions)

    # Do CSV ouput
    if html.output_format == "csv_export" and config.user.may(
            "general.csv_export"):
        _output_csv(what, av_mode, av_data, avoptions)
        return

    title += " - " + range_title

    if display_options.enabled(display_options.H):
        html.body_start(title, force=True)

    if display_options.enabled(display_options.T):
        html.top_heading(title)

    html.write(confirmation_html_code)

    # Remove variables for editing annotations, otherwise they will make it into the uris
    html.request.del_vars("anno_")
    if html.request.var("filled_in") == "editanno":
        html.request.del_var("filled_in")

    if display_options.enabled(display_options.B):
        html.begin_context_buttons()
        html.toggle_button(
            "avoptions",
            html.has_user_errors(),
            "painteroptions",
            _("Configure details of the report"),
        )
        html.context_button(
            _("Status View"),
            html.makeuri([("mode", "status")]),
            "status",
        )
        if config.reporting_available() and config.user.may(
                "general.reporting"):
            html.context_button(
                _("Export as PDF"),
                html.makeuri([], filename="report_instant.py"),
                "report",
            )

        if config.user.may("general.csv_export"):
            html.context_button(
                _("Export as CSV"),
                html.makeuri([("output_format", "csv_export")]),
                "download_csv",
            )

        if av_mode == "timeline" or av_object:
            html.context_button(
                _("Availability"),
                html.makeuri([("av_mode", "availability"), ("av_host", ""),
                              ("av_aggr", "")]),
                "availability",
            )
        elif not av_object:
            html.context_button(
                _("Timeline"),
                html.makeuri([("av_mode", "timeline")]),
                "timeline",
            )
        elif av_mode == "timeline" and what != "bi":
            history_url = availability.history_url_of(av_object, time_range)
            html.context_button(
                _("History"),
                history_url,
                "history",
            )

        html.end_context_buttons()

    # Render the avoptions again to get the HTML code, because the HTML vars have changed
    # above (anno_ and editanno_ has been removed, which must not be part of the form
    avoptions = render_availability_options(what)

    if not html.has_user_errors():
        # If we abolish the limit we have to fetch the data again
        # with changed logrow_limit = 0, which means no limit
        if has_reached_logrow_limit:
            text = _(
                "Your query matched more than %d log entries. "
                "<b>Note:</b> The number of shown rows does not necessarily reflect the "
                "matched entries and the result might be incomplete. "
            ) % avoptions["logrow_limit"]
            text += html.render_a(
                _('Repeat query without limit.'),
                html.makeuri([("_unset_logrow_limit", "1"),
                              ("avo_logrow_limit", 0)]))
            html.show_warning(text)

        do_render_availability(what, av_rawdata, av_data, av_mode, av_object,
                               avoptions)

    if display_options.enabled(display_options.Z):
        html.bottom_footer()

    if display_options.enabled(display_options.H):
        html.body_end()
Example #14
0
def edit_dictionaries(
        dictionaries,  # type: List[Tuple[str, Union[Transform, Dictionary]]]
        value,  # type: Dict[str, Any]
        focus=None,  # type: Optional[str]
        hover_help=True,  # type: bool
        validate=None,  # type: Optional[Callable[[Any], None]]
        buttontext=None,  # type: Optional[Text]
        title=None,  # type: Optional[Text]
        buttons=None,  # type: List[Tuple[str, Text, str]]
        method="GET",  # type: str
        preview=False,  # type: bool
        varprefix="",  # type: str
        formname="form",  # type: str
        consume_transid=True  # type: bool
):

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

        messages = []  # type: List[Text]
        new_value = {}  # type: 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))
                html.add_user_error(e.varname, e)
            except Exception as e:
                messages.append("%s: %s" %
                                (vs_dict.title() or _("Properties"), e))
                html.add_user_error(None, e)

            if validate and not html.has_user_errors():
                try:
                    validate(new_value[keyname])
                except MKUserError as e:
                    messages.append("%s" % e)
                    html.add_user_error(e.varname, 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()
    if buttons:
        for name, button_title, _icon in buttons:
            html.button(name, button_title)
    else:
        if buttontext is None:
            buttontext = _("Save")
        html.button("save", buttontext)
    # Should be ignored be hidden_fields, but I do not dare to change it there
    html.request.del_var("filled_in")
    html.hidden_fields()
    html.end_form()
Example #15
0
def _show_page_user_profile(change_pw):
    start_async_replication = False

    if not config.user.id:
        raise MKUserError(None, _('Not logged in.'))

    if not config.user.may('general.edit_profile') and not config.user.may(
            'general.change_password'):
        raise MKAuthException(
            _("You are not allowed to edit your user profile."))

    if not config.wato_enabled:
        raise MKAuthException(
            _('User profiles can not be edited (WATO is disabled).'))

    success = None
    if html.request.has_var('_save') and html.check_transaction():
        users = userdb.load_users(lock=True)

        try:
            # Profile edit (user options like language etc.)
            if config.user.may('general.edit_profile'):
                if not change_pw:
                    set_lang = html.get_checkbox('_set_lang')
                    language = html.request.var('language')
                    # Set the users language if requested
                    if set_lang:
                        if language == '':
                            language = None
                        # Set custom language
                        users[config.user.id]['language'] = language
                        config.user.language = language
                        html.set_language_cookie(language)

                    else:
                        # Remove the customized language
                        if 'language' in users[config.user.id]:
                            del users[config.user.id]['language']
                        config.user.reset_language()

                    # load the new language
                    cmk.gui.i18n.localize(config.user.language)

                    user = users.get(config.user.id)
                    if config.user.may('general.edit_notifications'
                                       ) and user.get("notifications_enabled"):
                        value = forms.get_input(
                            watolib.get_vs_flexible_notifications(),
                            "notification_method")
                        users[config.user.id]["notification_method"] = value

                    # Custom attributes
                    if config.user.may('general.edit_user_attributes'):
                        for name, attr in userdb.get_user_attributes():
                            if attr.user_editable():
                                if not attr.permission() or config.user.may(
                                        attr.permission()):
                                    vs = attr.valuespec()
                                    value = vs.from_html_vars('ua_' + name)
                                    vs.validate_value(value, "ua_" + name)
                                    users[config.user.id][name] = value

            # Change the password if requested
            password_changed = False
            if config.user.may('general.change_password'):
                cur_password = html.request.var('cur_password')
                password = html.request.var('password')
                password2 = html.request.var('password2', '')

                if change_pw:
                    # Force change pw mode
                    if not cur_password:
                        raise MKUserError(
                            "cur_password",
                            _("You need to provide your current password."))
                    if not password:
                        raise MKUserError(
                            "password", _("You need to change your password."))
                    if cur_password == password:
                        raise MKUserError(
                            "password",
                            _("The new password must differ from your current one."
                              ))

                if cur_password and password:
                    if userdb.hook_login(config.user.id,
                                         cur_password) is False:
                        raise MKUserError("cur_password",
                                          _("Your old password is wrong."))
                    if password2 and password != password2:
                        raise MKUserError(
                            "password2",
                            _("The both new passwords do not match."))

                    watolib.verify_password_policy(password)
                    users[config.user.id]['password'] = hash_password(password)
                    users[config.user.id]['last_pw_change'] = int(time.time())

                    if change_pw:
                        # Has been changed, remove enforcement flag
                        del users[config.user.id]['enforce_pw_change']

                    # Increase serial to invalidate old cookies
                    if 'serial' not in users[config.user.id]:
                        users[config.user.id]['serial'] = 1
                    else:
                        users[config.user.id]['serial'] += 1

                    password_changed = True

            # Now, if in distributed environment where users can login to remote sites,
            # set the trigger for pushing the new auth information to the slave sites
            # asynchronous
            if config.user.authorized_login_sites():
                start_async_replication = True

            userdb.save_users(users)

            if password_changed:
                # Set the new cookie to prevent logout for the current user
                login.set_auth_cookie(config.user.id)

            success = True
        except MKUserError as e:
            html.add_user_error(e.varname, e)
    else:
        users = userdb.load_users()

    watolib.init_wato_datastructures(with_wato_lock=True)

    # When in distributed setup, display the replication dialog instead of the normal
    # profile edit dialog after changing the password.
    if start_async_replication:
        user_profile_async_replication_page()
        return

    if change_pw:
        title = _("Change Password")
    else:
        title = _("Edit User Profile")

    html.header(title)

    # Rule based notifications: The user currently cannot simply call the according
    # WATO module due to WATO permission issues. So we cannot show this button
    # right now.
    if not change_pw:
        rulebased_notifications = watolib.load_configuration_settings().get(
            "enable_rulebased_notifications")
        if rulebased_notifications and config.user.may(
                'general.edit_notifications'):
            html.begin_context_buttons()
            url = "wato.py?mode=user_notifications_p"
            html.context_button(_("Notifications"), url, "notifications")
            html.end_context_buttons()
    else:
        reason = html.request.var('reason')
        if reason == 'expired':
            html.p(
                _('Your password is too old, you need to choose a new password.'
                  ))
        else:
            html.p(
                _('You are required to change your password before proceeding.'
                  ))

    if success:
        html.reload_sidebar()
        if change_pw:
            html.show_message(_("Your password has been changed."))
            raise HTTPRedirect(html.request.var('_origtarget', 'index.py'))
        else:
            html.show_message(_("Successfully updated user profile."))
            # Ensure theme changes are applied without additional user interaction
            html.immediate_browser_redirect(0.5, html.makeuri([]))

    if html.has_user_errors():
        html.show_user_errors()

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

    # Returns true if an attribute is locked and should be read only. Is only
    # checked when modifying an existing user
    locked_attributes = userdb.locked_attributes(user.get('connector'))

    def is_locked(attr):
        return attr in locked_attributes

    html.begin_form("profile", method="POST")
    html.prevent_password_auto_completion()
    html.open_div(class_="wato")
    forms.header(_("Personal Settings"))

    if not change_pw:
        forms.section(_("Name"), simple=True)
        html.write_text(user.get("alias", config.user.id))

    if config.user.may(
            'general.change_password') and not is_locked('password'):
        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")

    if not change_pw and config.user.may('general.edit_profile'):
        select_language(user)

        # Let the user configure how he wants to be notified
        if not rulebased_notifications \
            and config.user.may('general.edit_notifications') \
            and user.get("notifications_enabled"):
            forms.section(_("Notifications"))
            html.help(
                _("Here you can configure how you want to be notified about host and service problems and "
                  "other monitoring events."))
            watolib.get_vs_flexible_notifications().render_input(
                "notification_method", user.get("notification_method"))

        if config.user.may('general.edit_user_attributes'):
            for name, attr in userdb.get_user_attributes():
                if attr.user_editable():
                    vs = attr.valuespec()
                    forms.section(_u(vs.title()))
                    value = user.get(name, vs.default_value())
                    if not attr.permission() or config.user.may(
                            attr.permission()):
                        vs.render_input("ua_" + name, value)
                        html.help(_u(vs.help()))
                    else:
                        html.write(vs.value_to_text(value))

    # Save button
    forms.end()
    html.button("_save", _("Save"))
    html.close_div()
    html.hidden_fields()
    html.end_form()
    html.footer()
Example #16
0
def _wato_page_handler(current_mode: str,
                       mode_permissions: List[PermissionName],
                       mode_class: Type[WatoMode]) -> None:
    try:
        init_wato_datastructures(with_wato_lock=not html.is_transaction())
    except Exception:
        # Snapshot must work in any case
        if current_mode == 'snapshot':
            pass
        else:
            raise

    # Check general permission for this mode
    if mode_permissions is not None and not config.user.may("wato.seeall"):
        _ensure_mode_permissions(mode_permissions)

    mode = mode_class()

    # Do actions (might switch mode)
    action_message: Optional[str] = None
    if html.is_transaction():
        try:
            config.user.need_permission("wato.edit")

            # Even if the user has seen this mode because auf "seeall",
            # he needs an explicit access permission for doing changes:
            if config.user.may("wato.seeall"):
                if mode_permissions:
                    _ensure_mode_permissions(mode_permissions)

            if cmk.gui.watolib.read_only.is_enabled(
            ) and not cmk.gui.watolib.read_only.may_override():
                raise MKUserError(None, cmk.gui.watolib.read_only.message())

            result = mode.action()
            if isinstance(result, tuple):
                newmode, action_message = result
            else:
                newmode = result

            # We assume something has been modified and increase the config generation ID by one.
            update_config_generation()

            # If newmode is False, then we shall immediately abort.
            # This is e.g. the case, if the page outputted non-HTML
            # data, such as a tarball (in the export function). We must
            # be sure not to output *any* further data in that case.
            if newmode is False:
                return

            # if newmode is not None, then the mode has been changed
            if newmode is not None:
                assert not isinstance(newmode, bool)
                if newmode == "":  # no further information: configuration dialog, etc.
                    if action_message:
                        html.show_message(action_message)
                        wato_html_footer()
                    return
                mode_permissions, mode_class = _get_mode_permission_and_class(
                    newmode)
                current_mode = newmode
                mode = mode_class()
                html.request.set_var("mode",
                                     newmode)  # will be used by makeuri

                # Check general permissions for the new mode
                if mode_permissions is not None and not config.user.may(
                        "wato.seeall"):
                    for pname in mode_permissions:
                        if '.' not in pname:
                            pname = "wato." + pname
                        config.user.need_permission(pname)

        except MKUserError as e:
            action_message = "%s" % e
            html.add_user_error(e.varname, action_message)

        except MKAuthException as e:
            reason = e.args[0]
            action_message = reason
            html.add_user_error(None, reason)

    breadcrumb = make_main_menu_breadcrumb(
        mode.main_menu()) + mode.breadcrumb()
    page_menu = mode.page_menu(breadcrumb)
    wato_html_head(title=mode.title(),
                   breadcrumb=breadcrumb,
                   page_menu=page_menu,
                   show_body_start=display_options.enabled(display_options.H),
                   show_top_heading=display_options.enabled(display_options.T))

    if not html.is_transaction() or (cmk.gui.watolib.read_only.is_enabled() and
                                     cmk.gui.watolib.read_only.may_override()):
        _show_read_only_warning()

    # Show outcome of action
    if html.has_user_errors():
        html.show_user_errors()
    elif action_message:
        html.show_message(action_message)

    # Show content
    mode.handle_page()

    if is_sidebar_reload_needed():
        html.reload_sidebar()

    if config.wato_use_git and html.is_transaction():
        do_git_commit()

    wato_html_footer(show_footer=display_options.enabled(display_options.Z),
                     show_body_end=display_options.enabled(display_options.H))
Example #17
0
def _wato_page_handler(current_mode: str,
                       mode_permissions: Optional[List[PermissionName]],
                       mode_class: Type[WatoMode]) -> None:
    try:
        init_wato_datastructures(with_wato_lock=not html.is_transaction())
    except Exception:
        # Snapshot must work in any case
        if current_mode == 'snapshot':
            pass
        else:
            raise

    # Check general permission for this mode
    if mode_permissions is not None and not config.user.may("wato.seeall"):
        _ensure_mode_permissions(mode_permissions)

    mode = mode_class()

    # Do actions (might switch mode)
    if html.is_transaction():
        try:
            config.user.need_permission("wato.edit")

            # Even if the user has seen this mode because auf "seeall",
            # he needs an explicit access permission for doing changes:
            if config.user.may("wato.seeall"):
                if mode_permissions:
                    _ensure_mode_permissions(mode_permissions)

            if cmk.gui.watolib.read_only.is_enabled(
            ) and not cmk.gui.watolib.read_only.may_override():
                raise MKUserError(None, cmk.gui.watolib.read_only.message())

            result = mode.action()
            if isinstance(result, (tuple, str, bool)):
                raise MKGeneralException(
                    f"WatoMode \"{current_mode}\" returns unsupported return value: {result!r}"
                )

            # We assume something has been modified and increase the config generation ID by one.
            update_config_generation()

            if config.wato_use_git:
                do_git_commit()

            # Handle two cases:
            # a) Don't render the page content after action
            #    (a confirm dialog is displayed by the action, or a non-HTML content was sent)
            # b) Redirect to another page
            if isinstance(result, FinalizeRequest):
                raise result

        except MKUserError as e:
            html.add_user_error(e.varname, str(e))

        except MKAuthException as e:
            reason = e.args[0]
            html.add_user_error(None, reason)

    breadcrumb = make_main_menu_breadcrumb(
        mode.main_menu()) + mode.breadcrumb()
    page_menu = mode.page_menu(breadcrumb)
    wato_html_head(title=mode.title(),
                   breadcrumb=breadcrumb,
                   page_menu=page_menu,
                   show_body_start=display_options.enabled(display_options.H),
                   show_top_heading=display_options.enabled(display_options.T))

    if not html.is_transaction() or (cmk.gui.watolib.read_only.is_enabled() and
                                     cmk.gui.watolib.read_only.may_override()):
        _show_read_only_warning()

    # Show outcome of failed action on this page
    if html.has_user_errors():
        html.show_user_errors()

    # Show outcome of previous page (that redirected to this one)
    for message in get_flashed_messages():
        html.show_message(message)

    # Show content
    mode.handle_page()

    if is_sidebar_reload_needed():
        html.reload_whole_page()

    wato_html_footer(show_body_end=display_options.enabled(display_options.H))
Example #18
0
def edit_dictionaries(dictionaries,
                      value,
                      focus=None,
                      hover_help=True,
                      validate=None,
                      buttontext=None,
                      title=None,
                      buttons=None,
                      method="GET",
                      preview=False,
                      varprefix="",
                      formname="form",
                      consume_transid=True):

    # Convert list of entries/dictionaries
    sections = []
    for keyname, d in dictionaries:
        if isinstance(d, list):
            sections.append((keyname, title or _("Properties"), d))
        else:
            sections.append((keyname, None,
                             d))  # valuespec Dictionary, title used from dict

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

        messages = []
        new_value = {}
        for keyname, _section_title, entries in sections:
            if isinstance(entries, list):
                new_value[keyname] = value.get(keyname, {}).copy()
                for name, vs in entries:
                    if len(sections) == 1:
                        vp = varprefix
                    else:
                        vp = keyname + "_" + varprefix
                    try:
                        v = vs.from_html_vars(vp + name)
                        vs.validate_value(v, vp + name)
                        new_value[keyname][name] = v
                    except MKUserError as e:
                        messages.append("%s: %s" % (vs.title(), e))
                        html.add_user_error(e.varname, e)

            else:
                new_value[keyname] = {}
                try:
                    edited_value = entries.from_html_vars(keyname)
                    entries.validate_value(edited_value, keyname)
                    new_value[keyname].update(edited_value)
                except MKUserError as e:
                    messages.append("%s: %s" %
                                    (entries.title() or _("Properties"), e))
                    html.add_user_error(e.varname, e)
                except Exception as e:
                    messages.append("%s: %s" %
                                    (entries.title() or _("Properties"), e))
                    html.add_user_error(None, e)

            if validate and not html.has_user_errors():
                try:
                    validate(new_value[keyname])
                except MKUserError as e:
                    messages.append(e)
                    html.add_user_error(e.varname, 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, title1, entries in sections:
        subvalue = value.get(keyname, {})
        if isinstance(entries, list):
            header(title1)
            first = True
            for name, vs in entries:
                section(vs.title())
                html.help(vs.help())
                if name in subvalue:
                    v = subvalue[name]
                else:
                    v = vs.default_value()
                if len(sections) == 1:
                    vp = varprefix
                else:
                    vp = keyname + "_" + varprefix
                vs.render_input(vp + name, v)
                if (not focus and first) or (name == focus):
                    vs.set_focus(vp + name)
                    first = False
        else:
            entries.render_input_as_form(keyname, subvalue)

    end()
    if buttons:
        for name, button_title, _icon in buttons:
            html.button(name, button_title)
    else:
        if buttontext is None:
            buttontext = _("Save")
        html.button("save", buttontext)
    # Should be ignored be hidden_fields, but I do not dare to change it there
    html.request.del_var("filled_in")
    html.hidden_fields()
    html.end_form()