def page_add_snapin() -> None: if not user.may("general.configure_sidebar"): raise MKGeneralException( _("You are not allowed to change the sidebar.")) title = _("Add sidebar element") breadcrumb = make_simple_page_breadcrumb( mega_menu_registry.menu_customize(), title) make_header(html, title, breadcrumb, _add_snapins_page_menu(breadcrumb)) used_snapins = _used_snapins() html.open_div(class_=["add_snapin"]) for name, snapin_class in sorted(snapin_registry.items()): if name in used_snapins: continue if not snapin_class.may_see(): continue # not allowed for this user html.open_div( class_="snapinadder", onmouseover="this.style.cursor='pointer';", onclick="window.top.cmk.sidebar.add_snapin('%s')" % name, ) html.open_div(class_=["snapin_preview"]) html.div("", class_=["clickshield"]) SidebarRenderer().render_snapin( UserSidebarSnapin.from_snapin_type_id(name)) html.close_div() html.div(snapin_class.description(), class_=["description"]) html.close_div() html.close_div() html.footer()
def show_log_list(): title = _("All problematic logfiles") breadcrumb = make_simple_page_breadcrumb( mega_menu_registry.menu_monitoring(), title) make_header(html, title, breadcrumb, _log_list_page_menu(breadcrumb)) if request.has_var("_ack") and not request.var("_do_actions") == _("No"): do_log_ack(site=None, host_name=None, file_name=None) return for site, host_name, logs in all_logs(): if not logs: continue all_logs_empty = not any( parse_file(site, host_name, file_name) for file_name in logs) if all_logs_empty: continue # Logfile vanished html.h3( HTMLWriter.render_a( host_name, href=makeuri( request, [("site", site), ("host", host_name)], ), ), class_="table", ) list_logs(site, host_name, logs) html.footer()
def _page_not_found() -> Response: # TODO: This is a page handler. It should not be located in generic application # object. Move it to another place if request.has_var("_plain_error"): html.write_text(_("Page not found")) else: title = _("Page not found") make_header( html, title, Breadcrumb([ BreadcrumbItem( title="Nowhere", url=None, ), BreadcrumbItem( title=title, url="javascript:document.location.reload(false)", ), ]), ) html.show_error(_("This page was not found. Sorry.")) html.footer() return response
def _action(self) -> None: assert user.id is not None users = userdb.load_users(lock=True) user_spec = users[user.id] language = request.get_ascii_input_mandatory("language", "") # Set the users language if requested to set it explicitly if language != "_default_": user_spec["language"] = language user.language = language set_language_cookie(request, response, language) else: if "language" in user_spec: del user_spec["language"] user.reset_language() # load the new language localize(user.language) if user.may("general.edit_notifications") and user_spec.get( "notifications_enabled"): value = _get_input(get_vs_flexible_notifications(), "notification_method") user_spec["notification_method"] = value # Custom attributes if user.may("general.edit_user_attributes"): for name, attr in userdb.get_user_attributes(): if not attr.user_editable(): continue perm_name = attr.permission() if perm_name and not user.may(perm_name): continue vs = attr.valuespec() value = vs.from_html_vars("ua_" + name) vs.validate_value(value, "ua_" + name) # TODO: Dynamically fiddling around with a TypedDict is a bit questionable user_spec[name] = value # type: ignore[literal-required] userdb.save_users(users, datetime.now()) flash(_("Successfully updated user profile.")) # In distributed setups with remote sites where the user can login, start the # user profile replication now which will redirect the user to the destination # page after completion. Otherwise directly open up the destination page. if user.authorized_login_sites(): back_url = "user_profile_replicate.py?back=user_profile.py" else: back_url = "user_profile.py" # Ensure theme changes are applied without additional user interaction html.reload_whole_page(back_url) html.footer() raise FinalizeRequest(code=200)
def _show_form(self) -> None: assert user.id is not None credentials = load_two_factor_credentials(user.id) webauthn_credentials = credentials["webauthn_credentials"] backup_codes = credentials["backup_codes"] html.begin_form("two_factor", method="POST") html.div("", id_="webauthn_message") forms.header(_("Credentials")) forms.section(_("Registered credentials"), simple=True) if webauthn_credentials: self._show_credentials(webauthn_credentials) else: html.i(_("No credentials registered")) forms.section(_("Backup codes"), simple=True) if backup_codes: html.p( _("You have %d unused backup codes left. You can use them as one-time password " "if your key is not available.") % len(backup_codes)) html.i( _("If you regenerate backup codes, you automatically invalidate the existing codes." )) else: html.i(_("No backup codes created yet.")) forms.end() html.hidden_fields() html.end_form() html.footer()
def _show_form(self) -> None: assert user.id is not None credentials = load_two_factor_credentials(user.id) credential_id = request.get_ascii_input_mandatory("_edit") credential = credentials["webauthn_credentials"].get(credential_id) if credential is None: raise MKUserError("_edit", _("The credential does not exist")) html.begin_form("profile", method="POST") html.prevent_password_auto_completion() html.open_div(class_="wato") self._valuespec(credential).render_input( "profile", { "registered_at": credential["registered_at"], "alias": credential["alias"], }, ) forms.end() html.close_div() html.hidden_field("_edit", credential_id) html.hidden_fields() html.end_form() html.footer()
def action(self) -> ActionResult: if request.var("_action") != "discard": return None if not transactions.check_transaction(): return None if not self._may_discard_changes(): return None if not self.has_changes(): return None # Now remove all currently pending changes by simply restoring the last automatically # taken snapshot. Then activate the configuration. This should revert all pending changes. file_to_restore = self._get_last_wato_snapshot_file() if not file_to_restore: raise MKUserError(None, _("There is no WATO snapshot to be restored.")) msg = _("Discarded pending changes (Restored %s)") % file_to_restore # All sites and domains can be affected by a restore: Better restart everything. _changes.add_change( "changes-discarded", msg, domains=ABCConfigDomain.enabled_domains(), need_restart=True, ) self._extract_snapshot(file_to_restore) activate_changes.execute_activate_changes([ d.get_domain_request([]) for d in ABCConfigDomain.enabled_domains() ]) for site_id in activation_sites(): self.confirm_site_changes(site_id) build_index_background() make_header( html, self.title(), breadcrumb=self.breadcrumb(), show_body_start=display_options.enabled(display_options.H), show_top_heading=display_options.enabled(display_options.T), ) html.open_div(class_="wato") html.show_message(_("Successfully discarded all pending changes.")) html.javascript("hide_changes_buttons();") html.footer() return FinalizeRequest(code=200)
def _show_page(self, acktime: float, failed_notifications: LivestatusResponse) -> None: title = _("Confirm failed notifications") breadcrumb = make_simple_page_breadcrumb(mega_menu_registry.menu_monitoring(), title) page_menu = self._page_menu(acktime, failed_notifications, breadcrumb) make_header(html, title, breadcrumb, page_menu) self._show_notification_table(failed_notifications) html.footer()
def page_werk(): load_werks() werk_id = request.get_integer_input_mandatory("werk") if werk_id not in g_werks: raise MKUserError("werk", _("This werk does not exist.")) werk = g_werks[werk_id] title = ("%s %s - %s") % (_("Werk"), render_werk_id( werk, with_link=False), werk["title"]) breadcrumb = make_main_menu_breadcrumb(mega_menu_registry["help_links"]) breadcrumb.append( BreadcrumbItem( title=_("Change log (Werks)"), url="change_log.py", )) breadcrumb.append(make_current_page_breadcrumb_item(title)) make_header(html, title, breadcrumb, _page_menu_werk(breadcrumb, werk)) html.open_table(class_=["data", "headerleft", "werks"]) def werk_table_row(caption, content, css=None): html.open_tr() html.th(caption) html.td(content, class_=css) html.close_tr() translator = cmk.utils.werks.WerkTranslator() werk_table_row(_("ID"), render_werk_id(werk, with_link=False)) werk_table_row(_("Title"), HTMLWriter.render_b(render_werk_title(werk))) werk_table_row(_("Component"), translator.component_of(werk)) werk_table_row(_("Date"), render_werk_date(werk)) werk_table_row(_("Checkmk Version"), werk["version"]) werk_table_row(_("Level"), translator.level_of(werk), css="werklevel werklevel%d" % werk["level"]) werk_table_row(_("Class"), translator.class_of(werk), css="werkclass werkclass%s" % werk["class"]) werk_table_row( _("Compatibility"), translator.compatibility_of(werk), css="werkcomp werkcomp%s" % werk["compatible"], ) werk_table_row(_("Description"), render_werk_description(werk), css="nowiki") html.close_table() html.footer()
def _show_form(self) -> None: assert user.id is not None users = userdb.load_users() user_spec: Optional[UserSpec] = users.get(user.id) if user_spec 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(_("Username"), simple=True) html.write_text(user_spec.get("user_id", user.id)) forms.section(_("Full name"), simple=True) html.write_text(user_spec.get("alias", "")) select_language(user_spec) # Let the user configure how he wants to be notified rulebased_notifications = rulebased_notifications_enabled() if (not rulebased_notifications and user.may("general.edit_notifications") and user_spec.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.")) get_vs_flexible_notifications().render_input( "notification_method", user_spec.get("notification_method")) if user.may("general.edit_user_attributes"): custom_user_attr_topics = get_user_attributes_by_topic() _show_custom_user_attr(user_spec, custom_user_attr_topics.get("personal", [])) forms.header(_("User interface settings")) _show_custom_user_attr( user_spec, custom_user_attr_topics.get("interface", [])) forms.end() html.close_div() html.hidden_fields() html.end_form() html.footer()
def show_create_view_dialog(next_url=None): vs_ds = DatasourceSelection() ds = "services" # Default selection title = _("Create view") breadcrumb = visuals.visual_page_breadcrumb("views", title, "create") make_header( html, title, breadcrumb, make_simple_form_page_menu( _("View"), breadcrumb, form_name="create_view", button_name="_save", save_title=_("Continue"), ), ) if request.var("_save") and transactions.check_transaction(): try: ds = vs_ds.from_html_vars("ds") vs_ds.validate_value(ds, "ds") if not next_url: next_url = makeuri( request, [("datasource", ds)], filename="create_view_infos.py", ) else: next_url = next_url + "&datasource=%s" % ds raise HTTPRedirect(next_url) except MKUserError as e: html.user_error(e) html.begin_form("create_view") html.hidden_field("mode", "create") forms.header(_("Select Datasource")) forms.section(vs_ds.title()) vs_ds.render_input("ds", ds) html.help(vs_ds.help()) forms.end() html.hidden_fields() html.end_form() html.footer()
def show_host_log_list(site, host_name): title = _("Logfiles of host %s") % host_name breadcrumb = _host_log_list_breadcrumb(host_name, title) make_header(html, title, breadcrumb, _host_log_list_page_menu(breadcrumb, site, host_name)) if request.has_var("_ack") and not request.var("_do_actions") == _("No"): do_log_ack(site, host_name, file_name=None) return html.open_table(class_=["data"]) list_logs(site, host_name, logfiles_of_host(site, host_name)) html.close_table() html.footer()
def _render_exception(e: Exception, title: str) -> Response: if plain_error(): return Response( response=[ "%s%s\n" % (("%s: " % title) if title else "", e), ], mimetype="text/plain", ) if not fail_silently(): make_header(html, title, Breadcrumb()) html.show_error(str(e)) html.footer() return response
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()
def page(self) -> None: row = self._get_crash_row() crash_info = self._get_crash_info(row) title = _("Crash report: %s") % self._crash_id breadcrumb = self._breadcrumb(title) make_header(html, title, breadcrumb, self._page_menu(breadcrumb, crash_info)) # Do not reveal crash context information to unauthenticated users or not permitted # users to prevent disclosure of internal information if not user.may("general.see_crash_reports"): html.show_error("<b>%s:</b> %s" % (_("Internal error"), crash_info["exc_value"])) html.p( _("An internal error occurred while processing your request. " "You can report this issue to your Checkmk administrator. " "Detailed information can be found on the crash report page " "or in <tt>var/log/web.log</tt>.")) html.footer() return if request.has_var("_report") and transactions.check_transaction(): details = self._handle_report_form(crash_info) else: details = ReportSubmitDetails(name="", mail="") if crash_info["crash_type"] == "gui": html.show_error("<b>%s:</b> %s" % (_("Internal error"), crash_info["exc_value"])) html.p( _("An internal error occured while processing your request. " "You can report this issue to the Checkmk team to help " "fixing this issue. Please use the form below for reporting." )) self._warn_about_local_files(crash_info) self._show_report_form(crash_info, details) self._show_crash_report(crash_info) self._show_crash_report_details(crash_info, row) html.footer()
def do_log_ack(site, host_name, file_name): sites.live().set_auth_domain("action") logs_to_ack = [] if not host_name and not file_name: # all logs on all hosts for this_site, this_host, logs in all_logs(): for int_filename in logs: file_display = form_file_to_ext(int_filename) logs_to_ack.append( (this_site, this_host, int_filename, file_display)) elif host_name and not file_name: # all logs on one host for int_filename in logfiles_of_host(site, host_name): file_display = form_file_to_ext(int_filename) logs_to_ack.append((site, host_name, int_filename, file_display)) elif host_name and file_name: # one log on one host int_filename = form_file_to_int(file_name) logs_to_ack = [(site, host_name, int_filename, form_file_to_ext(int_filename))] else: for this_site, this_host, logs in all_logs(): file_display = form_file_to_ext(file_name) if file_name in logs: logs_to_ack.append( (this_site, this_host, file_name, file_display)) ack_msg = _get_ack_msg(host_name, file_name) ack = request.var("_ack") if not user.may("general.act"): html.h1(_("Permission denied"), class_=["error"]) html.div(_("You are not allowed to acknowledge %s") % ack_msg, class_=["error"]) html.footer() return # filter invalid values if ack != "1": raise MKUserError("_ack", _("Invalid value for ack parameter.")) for this_site, this_host, int_filename, display_name in logs_to_ack: try: acknowledge_logfile(this_site, this_host, int_filename, display_name) except Exception as e: html.show_error( _("The log file <tt>%s</tt> of host <tt>%s</tt> could not be deleted: %s." ) % (display_name, this_host, e)) html.footer() return html.show_message("<b>%s</b><p>%s</p>" % (_("Acknowledged %s") % ack_msg, _("Acknowledged all messages in %s.") % ack_msg)) html.footer()
def page(self) -> cmk.gui.pages.PageResult: breadcrumb = make_simple_page_breadcrumb( mega_menu_registry["help_links"], self._title()) load_werks() werk_table_options = _werk_table_options_from_request() make_header( html, self._title(), breadcrumb, self._page_menu(breadcrumb, werk_table_options), ) for message in get_flashed_messages(): html.show_message(message) handle_acknowledgement() html.open_div(class_="wato") render_werks_table(werk_table_options) html.close_div() html.footer()
class UserLoginTwoFactor(Page): def page(self) -> None: assert user.id is not None html.render_headfoot = False html.add_body_css_class("login") html.add_body_css_class("two_factor") make_header(html, _("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) html.label( _("Two-factor authentication"), for_="webauthn_message", id_="label_2fa", class_="legend", ) html.div("", id_="webauthn_message") with foldable_container( treename="webauthn_backup_codes", id_="backup_container", isopen=False, title=_("Use backup code"), indent=False, save_state=False, ): html.label( "%s:" % _("Backup code"), id_="label_pass", class_=["legend"], for_="_backup_code", ) html.br() html.password_input("_backup_code", id_="input_pass", size=None) html.open_div(id_="button_text") html.button("_use_backup_code", _("Use backup code"), cssclass="hot") html.close_div() html.close_div() if user_errors: html.open_div(id_="login_error") html.show_user_errors() html.close_div() html.javascript("cmk.webauthn.login()") html.hidden_fields() html.end_form() html.close_div() html.footer()
def show_file(site, host_name, file_name): int_filename = form_file_to_int(file_name) title = _("Logfiles of Host %s: %s") % (host_name, int_filename) breadcrumb = _show_file_breadcrumb(host_name, title) make_header( html, title, breadcrumb, _show_file_page_menu(breadcrumb, site, host_name, int_filename)) if request.has_var("_ack") and not request.var("_do_actions") == _("No"): do_log_ack(site, host_name, file_name) return try: log_chunks = parse_file(site, host_name, int_filename, hidecontext=request.var("_hidecontext", "no") == "yes") except Exception as e: if active_config.debug: raise html.show_error(_("Unable to show logfile: <b>%s</b>") % e) html.footer() return if log_chunks is None: html.show_error(_("The logfile does not exist on site.")) html.footer() return if log_chunks == []: html.show_message( _("This logfile contains no unacknowledged messages.")) html.footer() return html.open_div(id_="logwatch") for log in log_chunks: html.open_table(class_="groupheader") html.open_tr() html.td(form_level(log["level"]), class_=form_level(log["level"])) html.td(form_datetime(log["datetime"]), class_="date") html.close_tr() html.close_table() html.open_table(class_=["section"]) for line in log["lines"]: html.open_tr(class_=line["class"]) html.open_td(class_="lines") html.icon_button( analyse_url(site, host_name, int_filename, line["line"]), _("Analyze this line"), "analyze", ) html.write_text(line["line"].replace(" ", " ").replace( "\1", "<br>")) html.close_td() html.close_tr() html.close_table() html.close_div() html.footer()
def page_graph(): host_name = HostName(request.get_str_input_mandatory("host")) service = request.get_str_input_mandatory("service") dsname = request.get_str_input_mandatory("dsname") breadcrumb = make_service_breadcrumb(host_name, service) make_header( html, _("Prediction for %s - %s - %s") % (host_name, service, dsname), breadcrumb, ) # Get current value from perf_data via Livestatus current_value = get_current_perfdata(host_name, service, dsname) prediction_store = prediction.PredictionStore(host_name, service, dsname) timegroup, choices = _load_prediction_information( tg_name=request.var("timegroup"), prediction_store=prediction_store, ) html.begin_form("prediction") html.write_text(_("Show prediction for ")) html.dropdown("timegroup", choices, deflt=timegroup.name, onchange="document.prediction.submit();") html.hidden_fields() html.end_form() # Get prediction data tg_data = prediction_store.get_data(timegroup.name) if tg_data is None: raise MKGeneralException(_("Missing prediction data.")) swapped = swap_and_compute_levels(tg_data, timegroup.params) vertical_range = compute_vertical_range(swapped) legend = [ ("#000000", _("Reference")), ("#ffffff", _("OK area")), ("#ffff00", _("Warning area")), ("#ff0000", _("Critical area")), ] if current_value is not None: legend.append(("#0000ff", _("Current value: %.2f") % current_value)) create_graph(timegroup.name, graph_size, timegroup.range, vertical_range, legend) if "levels_upper" in timegroup.params: render_dual_area(swapped["upper_warn"], swapped["upper_crit"], "#fff000", 0.4) render_area_reverse(swapped["upper_crit"], "#ff0000", 0.1) if "levels_lower" in timegroup.params: render_dual_area(swapped["lower_crit"], swapped["lower_warn"], "#fff000", 0.4) render_area(swapped["lower_crit"], "#ff0000", 0.1) vscala_low = vertical_range[0] vscala_high = vertical_range[1] vert_scala = compute_vertical_scala(vscala_low, vscala_high) time_scala = [[timegroup.range[0] + i * 3600, "%02d:00" % i] for i in range(0, 25, 2)] render_coordinates(vert_scala, time_scala) if "levels_lower" in timegroup.params: render_dual_area(swapped["average"], swapped["lower_warn"], "#ffffff", 0.5) render_curve(swapped["lower_warn"], "#e0e000", square=True) render_curve(swapped["lower_crit"], "#f0b0a0", square=True) if "levels_upper" in timegroup.params: render_dual_area(swapped["upper_warn"], swapped["average"], "#ffffff", 0.5) render_curve(swapped["upper_warn"], "#e0e000", square=True) render_curve(swapped["upper_crit"], "#f0b0b0", square=True) render_curve(swapped["average"], "#000000") render_curve(swapped["average"], "#000000") # repetition makes line bolder # Try to get current RRD data and render it also from_time, until_time = timegroup.range now = time.time() if from_time <= now <= until_time: timeseries = prediction.get_rrd_data(host_name, service, dsname, "MAX", from_time, until_time) rrd_data = timeseries.values render_curve(rrd_data, "#0000ff", 2) if current_value is not None: rel_time = (now - prediction.timezone_at(now)) % timegroup.slice render_point(timegroup.range[0] + rel_time, current_value, "#0000ff") html.footer()
def user_profile_async_replication_page(back_url: str) -> None: sites = list(user.authorized_login_sites().keys()) user_profile_async_replication_dialog(sites=sites, back_url=back_url) html.footer()