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 = forms.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) 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 _get_event_stats_query(self, context_filters): # In case the user is not allowed to see unrelated events ec_filters = "" if not user.may("mkeventd.seeall") and not user.may("mkeventd.seeunrelated"): ec_filters = "Filter: event_contact_groups != \nFilter: host_name != \nOr: 2\n" event_query = ( # "Events" column "GET eventconsoleevents\n" "Stats: event_phase = open\n" "Stats: event_phase = ack\n" "StatsOr: 2\n" # "Problems" column "Stats: event_phase = open\n" "Stats: event_phase = ack\n" "StatsOr: 2\n" "Stats: event_state != 0\n" "StatsAnd: 2\n" # "Unhandled" column "Stats: event_phase = open\n" "Stats: event_state != 0\n" "Stats: event_host_in_downtime != 1\n" "StatsAnd: 3\n" + ec_filters + context_filters ) # Do not mark the site as dead in case the Event Console is not available. return livestatus.Query( event_query, suppress_exceptions=( livestatus.MKLivestatusTableNotFoundError, livestatus.MKLivestatusBadGatewayError, ), )
def __init__(self, url_checker: URLChecker[T]) -> None: self._category_permissions = { "global_settings": user.may("wato.global") or user.may("wato.seeall"), "folders": user.may("wato.hosts"), "hosts": user.may("wato.hosts"), "event_console": user.may("mkeventd.edit") or user.may("wato.seeall"), "event_console_settings": user.may("mkeventd.config") or user.may("wato.seeall"), "logfile_pattern_analyzer": user.may("wato.pattern_editor") or user.may("wato.seeall"), } self._url_checker = url_checker
def _may_activate_changes(self) -> bool: if not user.may("wato.activate"): return False if not user.may("wato.activateforeign") and self._has_foreign_changes_on_any_site(): return False if read_only.is_enabled() and not read_only.may_override(): return False return True
def _activation_form(self): if not user.may("wato.activate"): html.show_warning( _("You are not permitted to activate configuration changes.")) return if not self._changes: return if not user.may("wato.activateforeign" ) and self._has_foreign_changes_on_any_site(): html.show_warning( _("Sorry, you are not allowed to activate changes of other users." )) return valuespec = _vs_activation(self.title(), self.has_foreign_changes()) html.begin_form("activate", method="POST", action="") html.hidden_field("activate_until", self._get_last_change_id(), id_="activate_until") if valuespec: title = valuespec.title() assert title is not None forms.header(title) valuespec.render_input("activate", self._value) valuespec.set_focus("activate") html.help(valuespec.help()) if self.has_foreign_changes(): if user.may("wato.activateforeign"): html.show_warning( _("There are some changes made by your colleagues that you will " "activate if you proceed. You need to enable the checkbox above " "to confirm the activation of these changes.")) else: html.show_warning( _("There are some changes made by your colleagues that you can not " "activate because you are not permitted to. You can only activate " "the changes on the sites that are not affected by these changes. " "<br>" "If you need to activate your changes on all sites, please contact " "a permitted user to do it for you.")) forms.end() html.hidden_field("selection_id", weblib.selection_id()) html.hidden_fields() html.end_form() init_rowselect(self.name())
def __init__(self) -> None: super().__init__() if not user.id: raise MKUserError(None, _("Not logged in.")) if not user.may("general.change_password") and not user.may( "general.edit_profile"): raise MKAuthException( _("You are not allowed to edit your user profile.")) if not active_config.wato_enabled: raise MKAuthException( _("User profiles can not be edited (WATO is disabled)."))
def may_see(self): """Whether or not the currently logged in user is allowed to see this module""" if not self.enabled: return False if self.permission is None: return True if "." not in self.permission: permission = "wato." + self.permission else: permission = self.permission return user.may(permission) or user.may("wato.seeall")
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 may_delete(self): if not self.is_deletable(): return False if not self.is_stoppable() and self.is_active(): return False if not user.may("background_jobs.delete_jobs"): return False if self.is_foreign( ) and not user.may("background_jobs.delete_foreign_jobs"): return False return True
def verify_permission(host_name: HostName, site: Optional[livestatus.SiteId]) -> None: if user.may("general.see_all"): return query = "GET hosts\nFilter: host_name = %s\nStats: state >= 0%s" % ( livestatus.lqencode(host_name), "\nAuthUser: %s" % livestatus.lqencode(user.id) if user.id else "", ) if site: sites.live().set_only_sites([site]) try: result = sites.live().query_summed_stats(query, "ColumnHeaders: off\n") except livestatus.MKLivestatusNotFoundError: raise MKAuthException( _("No such inventory tree of host %s." " You may also have no access to this host.") % host_name) finally: if site: sites.live().set_only_sites() if result[0] == 0: raise MKAuthException( _("You are not allowed to access the host %s.") % host_name)
def _call_auth() -> Response: with login.authenticate(request) as authenticated: if not authenticated: return _handle_not_authenticated() # When displaying the crash report message, the user authentication context # has already been left. We need to preserve this information to be able to # show the correct message for the current user. g.may_see_crash_reports = user.may("general.see_crash_reports") # This may raise an exception with error messages, which will then be displayed to the user. _ensure_general_access() # Initialize the multisite cmk.gui.i18n. This will be replaced by # language settings stored in the user profile after the user # has been initialized _localize_request() # Update the UI theme with the attribute configured by the user. # Returns None on first load assert user.id is not None theme.set( cmk.gui.userdb.load_custom_attr(user_id=user.id, key="ui_theme", parser=lambda x: x)) func() return response
def _show_sidebar(self) -> None: if not user.may("general.see_sidebar"): html.div("", id_="check_mk_navigation") return user_config = UserSidebarConfig(user, active_config.sidebar) html.open_div( id_="check_mk_navigation", class_="min" if user.get_attribute("nav_hide_icons_title") else None, ) self._show_sidebar_head() html.close_div() assert user.id is not None sidebar_position = cmk.gui.userdb.load_custom_attr( user.id, "ui_sidebar_position", lambda x: None if x == "None" else "left") html.open_div(id_="check_mk_sidebar", class_=[sidebar_position]) self._show_snapin_bar(user_config) html.close_div() if user_config.folded: html.final_javascript("cmk.sidebar.fold_sidebar();")
def page_message(): if not user.may("general.message"): raise MKAuthException(_("You are not allowed to use the message module.")) title = _("Send user message") breadcrumb = make_simple_page_breadcrumb(mega_menu_registry.menu_setup(), title) menu = _page_menu(breadcrumb) html.header(title, breadcrumb, menu) vs_message = _vs_message() if transactions.check_transaction(): try: msg = vs_message.from_html_vars("_message") vs_message.validate_value(msg, "_message") _process_message_message(msg) except MKUserError as e: html.user_error(e) html.begin_form("message", method="POST") vs_message.render_input_as_form("_message", {}) html.hidden_fields() html.end_form() html.footer()
def ajax_openclose() -> None: response.set_content_type("application/json") if not user.may("general.configure_sidebar"): return None snapin_id = request.var("name") if snapin_id is None: return None state = request.var("state") if state not in [ SnapinVisibility.OPEN.value, SnapinVisibility.CLOSED.value, "off" ]: raise MKUserError("state", "Invalid state: %s" % state) user_config = UserSidebarConfig(user, active_config.sidebar) try: snapin = user_config.get_snapin(snapin_id) except KeyError: return None if state == "off": user_config.remove_snapin(snapin) else: snapin.visible = SnapinVisibility(state) user_config.save()
def page(self): if not user.may("general.configure_sidebar"): raise MKGeneralException( _("You are not allowed to change the sidebar.")) addname = request.var("name") if addname is None or addname not in snapin_registry: raise MKUserError(None, _("Invalid sidebar element %s") % addname) if addname in _used_snapins(): raise MKUserError(None, _("Element %s is already enabled") % addname) user_config = UserSidebarConfig(user, active_config.sidebar) snapin = UserSidebarSnapin.from_snapin_type_id(addname) user_config.add_snapin(snapin) user_config.save() with output_funnel.plugged(): try: url = SidebarRenderer().render_snapin(snapin) finally: snapin_code = output_funnel.drain() return { "name": addname, "url": url, "content": snapin_code, "refresh": snapin.snapin_type.refresh_regularly(), "restart": snapin.snapin_type.refresh_on_restart(), }
def _process_icons( what: IconObjectType, row: Row, tags: List[TagID], custom_vars: Dict[str, str], toplevel: bool, user_icon_ids: List[str], ) -> List[ABCIconEntry]: icons: List[ABCIconEntry] = [] for icon_id, icon in get_multisite_icons().items(): if icon.toplevel() != toplevel: continue if icon.type() == "custom_icon" and icon_id not in user_icon_ids: continue if not user.may("icons_and_actions.%s" % icon_id): continue try: for result in _process_icon(what, row, tags, custom_vars, icon_id, icon): icons.append(result) except Exception: icons.append( IconEntry( sort_index=icon.sort_index(), icon_name="alert", title=_("Exception in icon '%s': %s") % ( icon_id, traceback.format_exc(), ), ) ) return icons
def _page_menu_entries_setup(self) -> Iterator[PageMenuEntry]: if user.may("wato.sites"): yield PageMenuEntry( title=_("View changes"), icon_name="activate", item=make_simple_link(folder_preserving_link([("mode", "changelog")])), )
def move_snapin() -> None: response.set_content_type("application/json") if not user.may("general.configure_sidebar"): return None snapin_id = request.var("name") if snapin_id is None: return None user_config = UserSidebarConfig(user, active_config.sidebar) try: snapin = user_config.get_snapin(snapin_id) except KeyError: return None before_id = request.var("before") before_snapin: Optional[UserSidebarSnapin] = None if before_id: try: before_snapin = user_config.get_snapin(before_id) except KeyError: pass user_config.move_snapin_before(snapin, before_snapin) user_config.save()
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) html.header(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 _create_status_box( self, site_ids: Collection[livestatus.SiteId], css_class: str, site_status: str, ): html.open_div(class_="spacertop") html.open_div(class_=css_class) message_template = ungettext("%d site is %s.", "%d sites are %s.", len(site_ids)) message = message_template % (len(site_ids), site_status) tooltip_template = ungettext( "Associated hosts, services and events are not included " "in the Tactical Overview. The %s site is %s.", "Associated hosts, services and events are not included " "in the Tactical Overview. The %s sites are %s.", len(site_ids), ) tooltip = tooltip_template % (site_status, ", ".join(site_ids)) if user.may("wato.sites"): url = makeuri_contextless(request, [("mode", "sites")], filename="wato.py") html.icon_button(url, tooltip, "sites", target="main") html.a(message, target="main", href=url) else: html.icon("sites", tooltip) html.write_text(message) html.close_div() html.close_div()
def _ensure_general_access() -> None: if user.may("general.use"): return reason = [ _("You are not authorized to use the Check_MK GUI. Sorry. " "You are logged in as <b>%s</b>.") % user.id ] if user.role_ids: reason.append( _("Your roles are <b>%s</b>.") % ", ".join(user.role_ids)) else: reason.append(_("<b>You do not have any roles.</b>")) reason.append( _("If you think this is an error, please ask your administrator " "to check the permissions configuration.")) if login.auth_type == "cookie": # type: ignore[has-type] reason.append( _("<p>You have been logged out. Please reload the page " "to re-authenticate.</p>")) login.del_auth_cookie() raise MKAuthException(" ".join(reason))
def _show_command_form(datasource: ABCDataSource, rows: Rows) -> None: what = datasource.infos[0] html.javascript( """ $(document).ready(function() { $('.command_group').has('x').trigger('expand'); $('x').children().css('background-color', '#f84'); }); """ ) one_shown = False html.open_div(**{"data-role": "collapsible-set"}) for command_class in command_registry.values(): command = command_class() if what in command.tables and user.may(command.permission.name): html.open_div(class_=["command_group"], **{"data-role": "collapsible"}) html.h3(command.title) html.open_p() html.begin_form("actions") html.hidden_field("_do_actions", "yes") html.hidden_field("actions", "yes") command.render(what) html.hidden_fields() html.end_form() html.close_p() html.close_div() one_shown = True html.close_div() if not one_shown: html.write_text(_("No commands are possible in this view"))
def render_wato(mini): if not active_config.wato_enabled: html.write_text(_("Setup is disabled.")) return False if not user.may("wato.use"): html.write_text(_("You are not allowed to use the setup.")) return False menu = get_wato_menu_items() if mini: for topic in menu: for item in topic.items: html.icon_button( url=item.url, class_="show_more_mode" if item.is_show_more else None, title=item.title, icon=item.icon or "wato", target="main", ) else: show_topic_menu(treename="wato", menu=menu, show_item_icons=True) pending_info = watolib.get_pending_changes_info() if pending_info: footnotelinks([(pending_info, "wato.py?mode=changelog")]) html.div("", class_="clear")
def _page_menu_entry_acknowledge( site: Optional[SiteId] = None, host_name: Optional[HostName] = None, int_filename: Optional[str] = None, ) -> Iterator[PageMenuEntry]: if not user.may("general.act") or (host_name and not may_see(site, host_name)): return if int_filename: label = _("Clear log") else: label = _("Clear logs") urivars: HTTPVariables = [("_ack", "1")] if int_filename: urivars.append(("file", form_file_to_ext(int_filename))) ack_msg = _get_ack_msg( host_name, form_file_to_ext(int_filename) if int_filename else None) yield PageMenuEntry( title=label, icon_name="delete", item=make_simple_link( make_confirm_link( url=makeactionuri(request, transactions, urivars), message=_("Do you really want to acknowledge %s " "by <b>deleting</b> all stored messages?") % ack_msg, )), is_shortcut=True, is_suggested=True, )
def _page_menu_entries_export(self) -> Iterator[PageMenuEntry]: if not self._log_exists(): return if not user.may("wato.auditlog"): return if not user.may("wato.edit"): return if not user.may("general.csv_export"): return yield PageMenuEntry( title=_("Export CSV"), icon_name="download_csv", item=make_simple_link(makeactionuri(request, transactions, [("_action", "csv")])), )
def permissions_for_items(self) -> Mapping[str, Callable[[str], bool]]: return { "rules": self._permissions_rule, "hosts": lambda url: ( any(user.may(perm) for perm in ("wato.all_folders", "wato.see_all_folders")) or self._permissions_url(url) ), "setup": self._permissions_url, }
def _stats_query(cls) -> str: # In case the user is not allowed to see unrelated events ec_filters = "" if not user.may("mkeventd.seeall") and not user.may( "mkeventd.seeunrelated"): ec_filters = "\n".join([ "Filter: event_contact_groups != ", "Filter: host_name != ", "Or: 2", ]) return ("\n".join([ "GET eventconsoleevents", "Stats: event_state = 0", # ok "Stats: event_state = 1", # warning "Stats: event_state = 3", # unknown "Stats: event_state = 2", # critical ]) + ec_filters)
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 show(self): show_topic_menu(treename="views", menu=get_view_menu_items()) links = [] if user.may("general.edit_views"): if active_config.debug: links.append((_("Export"), "export_views.py")) links.append((_("Edit"), "edit_views.py")) footnotelinks(links)
def _page_menu_entries_setup(self) -> Iterator[PageMenuEntry]: if user.may("wato.sites"): yield PageMenuEntry( title=_("Sites"), icon_name="sites", item=make_simple_link( makeuri_contextless( request, [("mode", "sites")], )), ) if user.may("wato.auditlog"): yield PageMenuEntry( title=_("Audit log"), icon_name="auditlog", item=make_simple_link( folder_preserving_link([("mode", "auditlog")])), )