def page(self): html.begin_form("random") forms.header(_("Add random hosts")) forms.section(_("Number to create")) html.write_text("%s: " % _("Hosts to create in each folder")) html.text_input("count", default_value="10", cssclass="number") html.set_focus("count") html.br() html.write_text("%s: " % _("Number of folders to create in each level")) html.text_input("folders", default_value="10", cssclass="number") html.br() html.write_text("%s: " % _("Levels of folders to create")) html.text_input("levels", default_value="1", cssclass="number") forms.end() html.hidden_fields() html.end_form()
def page(self): html.help( _("The renaming of hosts is a complex operation since a host's name is being " "used as a unique key in various places. It also involves stopping and starting " "of the monitoring core. You cannot rename a host while you have pending changes." )) html.begin_form("rename_host", method="POST") forms.header(_("Rename host %s") % self._host.name()) forms.section(_("Current name")) html.write_text(self._host.name()) forms.section(_("New name")) html.text_input("newname", "") forms.end() html.set_focus("newname") html.button("rename", _("Rename host!"), "submit") html.hidden_fields() html.end_form()
def _show_try_form(self): html.begin_form('try') forms.header(_('Try Pattern Match')) forms.section(_('Hostname')) self._vs_host().render_input("host", self._hostname) forms.section(_('Logfile')) html.text_input('file') forms.section(_('Text to match')) html.help( _('You can insert some text (e.g. a line of the logfile) to test the patterns defined ' 'for this logfile. All patterns for this logfile are listed below. Matching patterns ' 'will be highlighted after clicking the "Try out" button.')) html.text_input('match', cssclass='match', size=100) forms.end() html.button('_try', _('Try out')) html.request.del_var('folder') # Never hand over the folder here html.hidden_fields() html.end_form()
def page(self): html.begin_form("random") forms.header(_("Create Random Hosts")) forms.section(_("Number to create")) html.write_text("%s: " % _("Hosts to create in each folder")) html.number_input("count", 10) html.set_focus("count") html.br() html.write_text("%s: " % _("Number of folders to create in each level")) html.number_input("folders", 10) html.br() html.write_text("%s: " % _("Levels of folders to create")) html.number_input("levels", 1) forms.end() html.button("start", _("Start!"), "submit") html.hidden_fields() html.end_form()
def _show_form(self) -> 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.')) 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()
def _activation_form(self): if not config.user.may("wato.activate"): html.show_warning(_("You are not permitted to activate configuration changes.")) return if not self._changes: return if not config.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 config.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_fields() html.end_form()
def _show_form(self) -> None: assert config.user.id is not None users = userdb.load_users() 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()
def page(self): hosts = get_hosts_from_checkboxes() html.p( _("You have selected <b>%d</b> hosts for bulk cleanup. This means removing " "explicit attribute values from hosts. The hosts will then inherit attributes " "configured at the host list or folders or simply fall back to the builtin " "default values.") % len(hosts)) html.begin_form("bulkcleanup", method="POST") forms.header(_("Attributes to remove from hosts")) if not self._select_attributes_for_bulk_cleanup(hosts): forms.end() html.write_text(_("The selected hosts have no explicit attributes")) else: forms.end() html.button("_save", _("Save & Finish")) html.hidden_fields() html.end_form()
def _show_try_form(self): html.begin_form("try") forms.header(_("Try Pattern Match")) forms.section(_("Hostname")) self._vs_host().render_input("host", self._hostname) forms.section(_("Logfile")) html.help(_("Here you need to insert the original file or pathname")) html.text_input("file", size=80) forms.section(_("Text to match")) html.help( _("You can insert some text (e.g. a line of the logfile) to test the patterns defined " "for this logfile. All patterns for this logfile are listed below. Matching patterns " 'will be highlighted after clicking the "Try out" button.')) html.text_input("match", cssclass="match", size=100) forms.end() html.button("_try", _("Try out")) request.del_var("folder") # Never hand over the folder here html.hidden_fields() html.end_form()
def page(self): self._folder.show_breadcrump() # Show search form html.begin_form("edit_host", method="GET") html.prevent_password_auto_completion() forms.header(_("General Properties")) forms.section(_("Hostname")) html.text_input("host_search_host") html.set_focus("host_search_host") # Attributes configure_attributes(False, {}, "host_search", parent=None, varprefix="host_search_") # Button forms.end() html.button("_local", _("Search in %s") % self._folder.title(), "submit") html.hidden_field("host_search", "1") html.hidden_fields() html.end_form()
def _download_table(self, title: str, paths: List[str]) -> None: forms.header(title) forms.container() for path in paths: os_path = path relpath = path.replace(cmk.utils.paths.agents_dir + '/', '') filename = path.split('/')[-1] file_size = os.stat(os_path).st_size # FIXME: Rename classes etc. to something generic html.open_div(class_="ruleset") html.open_div(style="width:300px;", class_="text") html.a(filename, href="agents/%s" % relpath, download=filename) html.span("." * 200, class_="dots") html.close_div() html.div(cmk.utils.render.fmt_bytes(file_size), style="width:60px;", class_="rulecount") html.close_div() html.close_div() forms.end()
def page(self) -> None: host_names = get_hostnames_from_checkboxes() hosts = {host_name: watolib.Folder.current().host(host_name) for host_name in host_names} current_host_hash = sha256(ensure_binary(repr(hosts))).hexdigest() # When bulk edit has been made with some hosts, then other hosts have been selected # and then another bulk edit has made, the attributes need to be reset before # rendering the form. Otherwise the second edit will have the attributes of the # first set. host_hash = request.var("host_hash") if not host_hash or host_hash != current_host_hash: request.del_vars(prefix="attr_") request.del_vars(prefix="bulk_change_") html.p( "%s%s %s" % ( _( "You have selected <b>%d</b> hosts for bulk edit. You can now change " "host attributes for all selected hosts at once. " ) % len(hosts), _( "If a select is set to <i>don't change</i> then currenty not all selected " "hosts share the same setting for this attribute. " "If you leave that selection, all hosts will keep their individual settings." ), _( "In case you want to <i>unset</i> attributes on multiple hosts, you need to " "use the <i>bulk cleanup</i> action instead of bulk edit." ), ) ) html.begin_form("edit_host", method="POST") html.prevent_password_auto_completion() html.hidden_field("host_hash", current_host_hash) configure_attributes(False, hosts, "bulk", parent=watolib.Folder.current()) forms.end() html.hidden_fields() html.end_form()
def _select_attributes_for_bulk_cleanup(self, hosts): attributes = self._get_attributes_for_bulk_cleanup(hosts) for attr, is_inherited, num_haveit in attributes: # Legend and Help forms.section(attr.title()) if attr.is_mandatory() and not is_inherited: html.write_text( _("This attribute is mandatory and there is no value " "defined in the host list or any parent folder.")) else: label = "clean this attribute on <b>%s</b> hosts" % \ (num_haveit == len(hosts) and "all selected" or str(num_haveit)) html.checkbox("_clean_%s" % attr.name(), False, label=label) html.help(attr.help()) forms.end() if not attributes: html.write_text(_("The selected hosts have no explicit attributes"))
def page(self): all_rulesets = watolib.AllRulesets() all_rulesets.load() for_host: bool = not self._service # Object type specific detail information if for_host: self._show_host_info() else: self._show_service_info(all_rulesets) last_maingroup = None for groupname in sorted( rulespec_group_registry.get_host_rulespec_group_names( for_host)): maingroup = groupname.split("/")[0] for rulespec in sorted(rulespec_registry.get_by_group(groupname), key=lambda x: x.title or ""): if (rulespec.item_type == "service") == (not self._service): continue # This rule is not for hosts/services # Open form for that group here, if we know that we have at least one rule if last_maingroup != maingroup: last_maingroup = maingroup rulegroup = watolib.get_rulegroup(maingroup) forms.header( rulegroup.title, isopen=maingroup == "monconf", narrow=True, css="rulesettings", ) html.help(rulegroup.help) self._output_analysed_ruleset(all_rulesets, rulespec, svc_desc_or_item=self._service, svc_desc=self._service) forms.end()
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 page(self) -> None: html.begin_form("group") forms.header(_("Properties")) forms.section(_("Name"), simple=not self._new, is_required=True) html.help( _("The name of the group is used as an internal key. It cannot be " "changed later. It is also visible in the status GUI.")) if self._new: html.text_input("name") html.set_focus("name") else: html.write_text(self._name) html.set_focus("alias") forms.section(_("Alias"), is_required=True) html.help(_("An alias or description of this group.")) html.text_input("alias", self.group["alias"]) self._show_extra_page_elements() forms.end() html.hidden_fields() html.end_form()
def page(self): # Show search form html.begin_form("edit_host", method="POST") html.prevent_password_auto_completion() basic_attributes = [ ("host_search_host", TextInput(title=_("Hostname", )), ""), ] html.set_focus("host_search_host") # Attributes configure_attributes( new=False, hosts={}, for_what="host_search", parent=None, varprefix="host_search_", basic_attributes=basic_attributes, ) forms.end() html.hidden_field("host_search", "1") html.hidden_fields() html.end_form()
def _show_start_form(self): html.begin_form("bulkinventory", method="POST") msgs = [] if self._all: vs = vs_bulk_discovery(render_form=True) else: # "Include subfolders" does not make sense for a selection of hosts # which is already given in the following situations: # - in the current folder below 'Selected hosts: Discovery' # - Below 'Bulk import' a automatic service discovery for # imported/selected hosts can be executed vs = vs_bulk_discovery(render_form=True, include_subfolders=False) msgs.append( _("You have selected <b>%d</b> hosts for bulk discovery.") % len(self._get_hosts_to_discover()) ) # The cast is needed for the moment, because mypy does not understand our data structure here selection = cast( Tuple[bool, bool, bool, bool], self._bulk_discovery_params["selection"] ) self._bulk_discovery_params["selection"] = [False] + list(selection[1:]) msgs.append( _( "The Checkmk discovery will automatically find and configure services " "to be checked on your hosts and may also discover host labels." ) ) html.open_p() html.write_text(" ".join(msgs)) vs.render_input("bulkinventory", self._bulk_discovery_params) forms.end() html.hidden_fields() html.end_form()
def page(self): # Show outcome of host validation. Do not validate new hosts errors = None if self._mode == "edit": errors = (watolib.validate_all_hosts([self._host.name()]).get( self._host.name(), []) + self._host.validation_errors()) if errors: html.open_div(class_="info") html.open_table(class_="validationerror", boder="0", cellspacing="0", cellpadding="0") html.open_tr() html.open_td(class_="img") html.icon("validation_error") html.close_td() html.open_td() html.open_p() html.h3(_("Warning: This host has an invalid configuration!")) html.open_ul() for error in errors: html.li(error) html.close_ul() html.close_p() if html.form_submitted(): html.br() html.b(_("Your changes have been saved nevertheless.")) html.close_td() html.close_tr() html.close_table() html.close_div() lock_message = "" locked_hosts = watolib.Folder.current().locked_hosts() if locked_hosts: if locked_hosts is True: lock_message = _( "Host attributes locked (You cannot edit this host)") elif isinstance(locked_hosts, str): lock_message = locked_hosts if lock_message: html.div(lock_message, class_="info") html.begin_form("edit_host", method="POST") html.prevent_password_auto_completion() basic_attributes = [ # attribute name, valuepec, default value ("host", self._vs_host_name(), self._host.name()), ] if self._is_cluster(): basic_attributes += [ # attribute name, valuepec, default value ( "nodes", self._vs_cluster_nodes(), self._host.cluster_nodes() if self._host else [], ), ] configure_attributes( new=self._mode != "edit", hosts={self._host.name(): self._host} if self._mode != "new" else {}, for_what="host" if not self._is_cluster() else "cluster", parent=watolib.Folder.current(), basic_attributes=basic_attributes, ) if self._mode != "edit": html.set_focus("host") forms.end() html.hidden_fields() html.end_form()
def _show_configuration_variables(self) -> None: search = self._search at_least_one_painted = False html.open_div(class_="globalvars") for group, config_variables in self.iter_all_configuration_variables(): header_is_painted = False # needed for omitting empty groups for config_variable in config_variables: varname = config_variable.ident() valuespec = config_variable.valuespec() if self._show_only_modified and varname not in self._current_settings: continue help_text = valuespec.help() or "" title_text = valuespec.title() or "" if (search and search not in group.title().lower() and search not in config_variable.domain().ident().lower() and search not in varname and search not in help_text.lower() and search not in title_text.lower()): continue # skip variable when search is performed and nothing matches at_least_one_painted = True if not header_is_painted: # always open headers when searching forms.header(group.title(), isopen=search or self._show_only_modified) header_is_painted = True default_value = self._default_values[varname] edit_url = folder_preserving_link([ ("mode", self.edit_mode_name), ("varname", varname), ("site", request.var("site", "")), ]) title = HTMLWriter.render_a( title_text, href=edit_url, class_="modified" if varname in self._current_settings else None, title=escaping.strip_tags(help_text), ) if varname in self._current_settings: value = self._current_settings[varname] elif varname in self._global_settings: value = self._global_settings[varname] else: value = default_value try: to_text = valuespec.value_to_html(value) except Exception: logger.exception("error converting %r to text", value) to_text = html.render_error( _("Failed to render value: %r") % value) # Is this a simple (single) value or not? change styling in these cases... simple = True if "\n" in to_text or "<td>" in to_text: simple = False forms.section(title, simple=simple) if varname in self._current_settings: modified_cls = ["modified"] value_title: Optional[str] = _( "This option has been modified.") elif varname in self._global_settings: modified_cls = ["modified globally"] value_title = _( "This option has been modified in global settings.") else: modified_cls = [] value_title = None if is_a_checkbox(valuespec): html.open_div(class_=["toggle_switch_container"] + modified_cls + (["on"] if value else [])) html.toggle_switch( enabled=value, help_txt=_("Immediately toggle this setting"), href=makeactionuri(request, transactions, [("_action", "toggle"), ("_varname", varname)]), class_=modified_cls, title=value_title, ) html.close_div() else: html.a(to_text, href=edit_url, class_=modified_cls, title=value_title) if header_is_painted: forms.end() if not at_least_one_painted and search: html.show_message( _("Did not find any global setting matching your search.")) html.close_div()
def page(self): html.begin_form("timeperiod", method="POST") self._valuespec().render_input("timeperiod", self._to_valuespec(self._timeperiod)) forms.end() html.hidden_fields() html.end_form()
def page(self): search = get_search_expression() html.begin_form("role", method="POST") # ID forms.header(_("Basic properties"), css="wide") forms.section(_("Internal ID"), simple="builtin" in self._role, is_required=True) if self._role.get("builtin"): html.write_text("%s (%s)" % (self._role_id, _("builtin role"))) html.hidden_field("id", self._role_id) else: html.text_input("id", self._role_id) html.set_focus("id") # Alias forms.section(_("Alias")) html.help(_("An alias or description of the role")) html.text_input("alias", self._role.get("alias", ""), size=50) # Based on if not self._role.get("builtin"): forms.section(_("Based on role")) html.help( _("Each user defined role is based on one of the builtin roles. " "When created it will start with all permissions of that role. When due to a software " "update or installation of an addons new permissions appear, the user role will get or " "not get those new permissions based on the default settings of the builtin role it's " "based on.")) role_choices: Choices = [(i, r["alias"]) for i, r in self._roles.items() if r.get("builtin")] html.dropdown("basedon", role_choices, deflt=self._role.get("basedon", "user"), ordered=True) forms.end() html.h2(_("Permissions")) # Permissions base_role_id = self._role.get("basedon", self._role_id) html.help( _("When you leave the permissions at "default" then they get their " "settings from the factory defaults (for builtin roles) or from the " "factory default of their base role (for user define roles). Factory defaults " "may change due to software updates. When choosing another base role, all " "permissions that are on default will reflect the new base role." )) for section in permission_section_registry.get_sorted_sections(): # Now filter by the optional search term filtered_perms = [] for perm in permission_registry.get_sorted_permissions(section): if search and (search not in perm.title.lower() and search not in perm.name.lower()): continue filtered_perms.append(perm) if not filtered_perms: continue forms.header(section.title, isopen=search is not None, css="wide") for perm in filtered_perms: forms.section(perm.title) pvalue = self._role["permissions"].get(perm.name) def_value = base_role_id in perm.defaults choices: Choices = [ ("yes", _("yes")), ("no", _("no")), ("default", _("default (%s)") % (def_value and _("yes") or _("no"))), ] deflt = {True: "yes", False: "no"}.get(pvalue, "default") html.dropdown("perm_" + perm.name, choices, deflt=deflt, style="width: 130px;") html.help(perm.description) forms.end() html.hidden_fields() html.end_form()
def _show_configuration_variables(self, groups): search = self._search at_least_one_painted = False html.open_div(class_="globalvars") for group in sorted(groups, key=lambda g: g.sort_index()): header_is_painted = False # needed for omitting empty groups for config_variable_class in group.config_variables(): config_variable = config_variable_class() varname = config_variable.ident() valuespec = config_variable.valuespec() if not config_variable.domain().enabled(): continue if config_variable.domain( ) == watolib.ConfigDomainCore and varname not in self._default_values: if config.debug: raise MKGeneralException( "The configuration variable <tt>%s</tt> is unknown to " "your local Check_MK installation" % varname) continue if not config_variable.in_global_settings(): continue if self._show_only_modified and varname not in self._current_settings: continue help_text = valuespec.help() or '' title_text = valuespec.title() if search and search not in group.title().lower() \ and search not in config_variable.domain().ident.lower() \ and search not in varname \ and search not in help_text.lower() \ and search not in title_text.lower(): continue # skip variable when search is performed and nothing matches at_least_one_painted = True if not header_is_painted: # always open headers when searching forms.header(group.title(), isopen=search or self._show_only_modified) header_is_painted = True default_value = self._default_values[varname] edit_url = watolib.folder_preserving_link([("mode", self._edit_mode()), ("varname", varname), ("site", html.request.var("site", ""))]) title = html.render_a( title_text, href=edit_url, class_="modified" if varname in self._current_settings else None, title=escaping.strip_tags(help_text)) if varname in self._current_settings: value = self._current_settings[varname] elif varname in self._global_settings: value = self._global_settings[varname] else: value = default_value try: to_text = valuespec.value_to_text(value) except Exception: logger.exception("error converting %r to text", value) to_text = html.render_error(_("Failed to render value: %r") % value) # Is this a simple (single) value or not? change styling in these cases... simple = True if '\n' in to_text or '<td>' in to_text: simple = False forms.section(title, simple=simple) if varname in self._current_settings: modified_cls: Optional[str] = "modified" value_title: Optional[str] = _("This option has been modified.") elif varname in self._global_settings: modified_cls = "modified globally" value_title = _("This option has been modified in global settings.") else: modified_cls = None value_title = None if is_a_checkbox(valuespec): html.open_div(class_=["toggle_switch_container", modified_cls]) html.toggle_switch( enabled=value, help_txt=_("Immediately toggle this setting"), href=html.makeactionuri([("_action", "toggle"), ("_varname", varname)]), class_=modified_cls, title=value_title, ) html.close_div() else: html.a(HTML(to_text), href=edit_url, class_=modified_cls, title=value_title) if header_is_painted: forms.end() if not at_least_one_painted and search: html.show_message(_('Did not find any global setting matching your search.')) html.close_div()
def page(self): # TODO: remove subclass specific things specifict things (everything with _type == 'user') html.begin_form("attr") forms.header(_("Properties")) forms.section(_("Name"), simple=not self._new) html.help( _("The name of the attribute is used as an internal key. It cannot be " "changed later.")) if self._new: html.text_input("name", self._attr.get('name')) html.set_focus("name") else: html.write_text(self._name) html.set_focus("title") forms.section(_("Title") + "<sup>*</sup>") html.help(_("The title is used to label this attribute.")) html.text_input("title", self._attr.get('title')) forms.section(_('Topic')) html.help( _('The attribute is added to this section in the edit dialog.')) html.dropdown('topic', self._topics, deflt=self._attr.get('topic', self._default_topic)) forms.section(_('Help Text') + "<sup>*</sup>") html.help( _('You might want to add some helpful description for the attribute.' )) html.text_area('help', self._attr.get('help', '')) forms.section(_('Data type')) html.help(_('The type of information to be stored in this attribute.')) if self._new: html.dropdown('type', custom_attr_types(), deflt=self._attr.get('type')) else: html.write(dict(custom_attr_types())[self._attr.get('type')]) self._add_extra_form_sections() forms.section(_('Show in WATO host table')) html.help( _('This attribute is only visibile on the edit host and folder pages by default, but ' 'you can also make it visible in the host overview tables of WATO.' )) html.checkbox( 'show_in_table', self._attr.get('show_in_table', False), label=_( "Show the setting of the attribute in the WATO host list table" )) forms.section(_('Add to monitoring configuration')) html.help(self._macro_help) html.checkbox('add_custom_macro', self._attr.get('add_custom_macro', False), label=self._macro_label) forms.end() html.show_localization_hint() html.button("save", _("Save")) html.hidden_fields() html.end_form()
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()
def page(self): # Show outcome of host validation. Do not validate new hosts errors = None if self._mode != "edit": watolib.Folder.current().show_breadcrump() else: errors = watolib.validate_all_hosts([self._host.name()]).get( self._host.name(), []) + self._host.validation_errors() if errors: html.open_div(class_="info") html.open_table(class_="validationerror", boder=0, cellspacing=0, cellpadding=0) html.open_tr() html.open_td(class_="img") html.icon(title=None, icon="validation_error") html.close_td() html.open_td() html.open_p() html.h3(_("Warning: This host has an invalid configuration!")) html.open_ul() for error in errors: html.li(error) html.close_ul() html.close_p() if html.form_submitted(): html.br() html.b(_("Your changes have been saved nevertheless.")) html.close_td() html.close_tr() html.close_table() html.close_div() lock_message = "" if watolib.Folder.current().locked_hosts(): if watolib.Folder.current().locked_hosts() is True: lock_message = _( "Host attributes locked (You cannot edit this host)") else: lock_message = watolib.Folder.current().locked_hosts() if len(lock_message) > 0: html.div(lock_message, class_="info") html.begin_form("edit_host", method="POST") html.prevent_password_auto_completion() forms.header(_("General Properties")) self._show_host_name() # Cluster: nodes if self._is_cluster(): forms.section(_("Nodes")) self._vs_cluster_nodes().render_input( "nodes", self._host.cluster_nodes() if self._host else []) html.help( _('Enter the host names of the cluster nodes. These ' 'hosts must be present in WATO. ')) configure_attributes( new=self._mode != "edit", hosts={self._host.name(): self._host} if self._mode != "new" else {}, for_what="host" if not self._is_cluster() else "cluster", parent=watolib.Folder.current()) forms.end() if not watolib.Folder.current().locked_hosts(): html.button("services", _("Save & go to Services"), "submit") html.button("save", _("Save & Finish"), "submit") if not self._is_cluster(): html.button("diag_host", _("Save & Test"), "submit") html.hidden_fields() html.end_form()
def _show_start_form(self): html.begin_form("parentscan", method="POST") html.hidden_fields() # Mode of action html.open_p() if not self._complete_folder: num_selected = len(get_hosts_from_checkboxes()) html.write_text(_("You have selected <b>%d</b> hosts for parent scan. ") % num_selected) html.p( _( "The parent scan will try to detect the last gateway " "on layer 3 (IP) before a host. This will be done by " "calling <tt>traceroute</tt>. If a gateway is found by " "that way and its IP address belongs to one of your " "monitored hosts, that host will be used as the hosts " "parent. If no such host exists, an artifical ping-only " "gateway host will be created if you have not disabled " "this feature." ) ) forms.header(_("Settings for Parent Scan")) self._settings = ParentScanSettings( **user.load_file( "parentscan", { "where": "subfolder", "alias": _("Created by parent scan"), "recurse": True, "select": "noexplicit", "timeout": 8, "probes": 2, "ping_probes": 5, "max_ttl": 10, "force_explicit": False, }, ) ) # Selection forms.section(_("Selection")) if self._complete_folder: html.checkbox("recurse", self._settings.recurse, label=_("Include all subfolders")) html.br() html.radiobutton( "select", "noexplicit", self._settings.select == "noexplicit", _("Skip hosts with explicit parent definitions (even if empty)") + "<br>", ) html.radiobutton( "select", "no", self._settings.select == "no", _("Skip hosts hosts with non-empty parents (also if inherited)") + "<br>", ) html.radiobutton( "select", "ignore", self._settings.select == "ignore", _("Scan all hosts") + "<br>" ) # Performance forms.section(_("Performance")) html.open_table() html.open_tr() html.open_td() html.write_text(_("Timeout for responses") + ":") html.close_td() html.open_td() html.text_input("timeout", str(self._settings.timeout), size=2, cssclass="number") html.write_text(_("sec")) html.close_td() html.close_tr() html.open_tr() html.open_td() html.write_text(_("Number of probes per hop") + ":") html.close_td() html.open_td() html.text_input("probes", str(self._settings.probes), size=2, cssclass="number") html.close_td() html.close_tr() html.open_tr() html.open_td() html.write_text(_("Maximum distance (TTL) to gateway") + ":") html.close_td() html.open_td() html.text_input("max_ttl", str(self._settings.max_ttl), size=2, cssclass="number") html.close_td() html.close_tr() html.open_tr() html.open_td() html.write_text(_("Number of PING probes") + ":") html.help( _( "After a gateway has been found, Check_MK checks if it is reachable " "via PING. If not, it is skipped and the next gateway nearer to the " "monitoring core is being tried. You can disable this check by setting " "the number of PING probes to 0." ) ) html.close_td() html.open_td() html.text_input("ping_probes", str(self._settings.ping_probes), size=2, cssclass="number") html.close_td() html.close_tr() html.close_table() # Configuring parent forms.section(_("Configuration")) html.checkbox( "force_explicit", deflt=self._settings.force_explicit, label=_( "Force explicit setting for parents even if setting matches that of the folder" ), ) # Gateway creation forms.section(_("Creation of gateway hosts")) html.write_text(_("Create gateway hosts in")) html.open_ul() html.radiobutton( "where", "subfolder", self._settings.where == "subfolder", _("in the subfolder <b>%s/Parents</b>") % Folder.current_disk_folder().title(), ) html.br() html.radiobutton( "where", "here", self._settings.where == "here", _("directly in the folder <b>%s</b>") % Folder.current_disk_folder().title(), ) html.br() html.radiobutton( "where", "there", self._settings.where == "there", _("in the same folder as the host") ) html.br() html.radiobutton( "where", "nowhere", self._settings.where == "nowhere", _("do not create gateway hosts") ) html.close_ul() html.write_text(_("Alias for created gateway hosts") + ": ") html.text_input("alias", default_value=self._settings.alias) forms.end() # Start button html.button("_start", _("Start")) html.hidden_fields() html.end_form()
def _show_form(self, profile_changed: bool) -> None: assert config.user.id is not None users = userdb.load_users() breadcrumb = make_simple_page_breadcrumb(MegaMenuUser, self._page_title()) html.header(self._page_title(), breadcrumb) 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.button("_save", _("Save")) html.close_div() html.hidden_fields() html.end_form() html.footer()
def _show_form(self, profile_changed: bool) -> None: assert config.user.id is not None users = userdb.load_users() breadcrumb = make_simple_page_breadcrumb(MegaMenuUser, self._page_title()) html.header(self._page_title(), breadcrumb) self._show_context_buttons() 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, 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 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.button("_save", _("Save")) html.close_div() html.hidden_fields() html.end_form() html.footer()
def page(self): # Let exceptions from loading notification scripts happen now watolib.load_notification_scripts() html.begin_form("user", method="POST") html.prevent_password_auto_completion() forms.header(_("Identity")) # ID forms.section(_("Username"), simple=not self._is_new_user) if self._is_new_user: vs_user_id = UserID(allow_empty=False) else: vs_user_id = FixedValue(self._user_id) vs_user_id.render_input("user_id", self._user_id) def lockable_input(name, dflt): if not self._is_locked(name): html.text_input(name, self._user.get(name, dflt), size=50) else: html.write_text(self._user.get(name, dflt)) html.hidden_field(name, self._user.get(name, dflt)) # Full name forms.section(_("Full name")) lockable_input('alias', self._user_id) html.help(_("Full name or alias of the user")) # Email address forms.section(_("Email address")) email = self._user.get("email", "") if not self._is_locked("email"): EmailAddressUnicode().render_input("email", email) else: html.write_text(email) html.hidden_field("email", email) html.help( _("The email address is optional and is needed " "if the user is a monitoring contact and receives notifications " "via Email.")) forms.section(_("Pager address")) lockable_input('pager', '') html.help(_("The pager address is optional ")) if cmk.is_managed_edition(): forms.section(self._vs_customer.title()) self._vs_customer.render_input("customer", managed.get_customer_id(self._user)) html.help(self._vs_customer.help()) vs_sites = self._vs_sites() forms.section(vs_sites.title()) authorized_sites = self._user.get("authorized_sites", vs_sites.default_value()) if not self._is_locked("authorized_sites"): vs_sites.render_input("authorized_sites", authorized_sites) else: html.write_html(vs_sites.value_to_text(authorized_sites)) html.help(vs_sites.help()) self._show_custom_user_attributes('ident') forms.header(_("Security")) forms.section(_("Authentication")) is_automation = self._user.get("automation_secret", None) is not None html.radiobutton("authmethod", "password", not is_automation, _("Normal user login with password")) html.open_ul() html.open_table() html.open_tr() html.td(_("password:"******"_password_" + self._pw_suffix(), autocomplete="new-password") html.close_td() html.close_tr() html.open_tr() html.td(_("repeat:")) html.open_td() html.password_input("_password2_" + self._pw_suffix(), autocomplete="new-password") html.write_text(" (%s)" % _("optional")) html.close_td() html.close_tr() html.open_tr() html.td("%s:" % _("Enforce change")) html.open_td() # Only make password enforcement selection possible when user is allowed to change the PW if self._is_new_user or config.user_may(self._user_id, 'general.edit_profile') and config.user_may( self._user_id, 'general.change_password'): html.checkbox("enforce_pw_change", self._user.get("enforce_pw_change", False), label=_("Change password at next login or access")) else: html.write_text( _("Not permitted to change the password. Change can not be enforced.")) else: html.i(_('The password can not be changed (It is locked by the user connector).')) html.hidden_field('_password', '') html.hidden_field('_password2', '') html.close_td() html.close_tr() html.close_table() html.close_ul() html.radiobutton("authmethod", "secret", is_automation, _("Automation secret for machine accounts")) html.open_ul() html.text_input("_auth_secret", self._user.get("automation_secret", ""), size=30, id_="automation_secret") html.write_text(" ") html.open_b(style=["position: relative", "top: 4px;"]) html.write(" ") html.icon_button("javascript:cmk.wato.randomize_secret('automation_secret', 20);", _("Create random secret"), "random") html.close_b() html.close_ul() html.help( _("If you want the user to be able to login " "then specify a password here. Users without a login make sense " "if they are monitoring contacts that are just used for " "notifications. The repetition of the password is optional. " "<br>For accounts used by automation processes (such as fetching " "data from views for further procession), set the method to " "<u>secret</u>. The secret will be stored in a local file. Processes " "with read access to that file will be able to use Multisite as " "a webservice without any further configuration.")) # Locking forms.section(_("Disable password"), simple=True) if not self._is_locked('locked'): html.checkbox("locked", self._user.get("locked", False), label=_("disable the login to this account")) else: html.write_text( _('Login disabled') if self._user.get("locked", False) else _('Login possible')) html.hidden_field('locked', '1' if self._user.get("locked", False) else '') html.help( _("Disabling the password will prevent a user from logging in while " "retaining the original password. Notifications are not affected " "by this setting.")) forms.section(_("Idle timeout")) idle_timeout = self._user.get("idle_timeout") if not self._is_locked("idle_timeout"): watolib.get_vs_user_idle_timeout().render_input("idle_timeout", idle_timeout) else: html.write_text(idle_timeout) html.hidden_field("idle_timeout", idle_timeout) # Roles forms.section(_("Roles")) entries = self._roles.items() entries.sort(key=lambda x: (x[1]["alias"], x[0])) is_member_of_at_least_one = False for role_id, role in entries: if not self._is_locked("roles"): html.checkbox("role_" + role_id, role_id in self._user.get("roles", [])) url = watolib.folder_preserving_link([("mode", "edit_role"), ("edit", role_id)]) html.a(role["alias"], href=url) html.br() else: is_member = role_id in self._user.get("roles", []) if is_member: is_member_of_at_least_one = True url = watolib.folder_preserving_link([("mode", "edit_role"), ("edit", role_id)]) html.a(role["alias"], href=url) html.br() html.hidden_field("role_" + role_id, '1' if is_member else '') if self._is_locked('roles') and not is_member_of_at_least_one: html.i(_('No roles assigned.')) self._show_custom_user_attributes('security') # Contact groups forms.header(_("Contact Groups"), isopen=False) forms.section() groups_page_url = watolib.folder_preserving_link([("mode", "contact_groups")]) group_assign_url = watolib.folder_preserving_link([("mode", "rulesets"), ("group", "grouping")]) if not self._contact_groups: html.write( _("Please first create some <a href='%s'>contact groups</a>") % groups_page_url) else: entries = sorted([(group['alias'] or c, c) for c, group in self._contact_groups.items() ]) is_member_of_at_least_one = False for alias, gid in entries: is_member = gid in self._user.get("contactgroups", []) if not self._is_locked('contactgroups'): html.checkbox("cg_" + gid, gid in self._user.get("contactgroups", [])) else: if is_member: is_member_of_at_least_one = True html.hidden_field("cg_" + gid, '1' if is_member else '') if not self._is_locked('contactgroups') or is_member: url = watolib.folder_preserving_link([("mode", "edit_contact_group"), ("edit", gid)]) html.a(alias, href=url) html.br() if self._is_locked('contactgroups') and not is_member_of_at_least_one: html.i(_('No contact groups assigned.')) html.help( _("Contact groups are used to assign monitoring " "objects to users. If you haven't defined any contact groups yet, " "then first <a href='%s'>do so</a>. Hosts and services can be " "assigned to contact groups using <a href='%s'>rules</a>.<br><br>" "If you do not put the user into any contact group " "then no monitoring contact will be created for the user.") % (groups_page_url, group_assign_url)) forms.header(_("Notifications"), isopen=False) if not self._rbn_enabled(): forms.section(_("Enabling"), simple=True) html.checkbox("notifications_enabled", self._user.get("notifications_enabled", False), label=_("enable notifications")) html.help( _("Notifications are sent out " "when the status of a host or service changes.")) # Notification period forms.section(_("Notification time period")) choices = [(id_, "%s" % (tp["alias"])) for (id_, tp) in self._timeperiods.items()] html.dropdown("notification_period", choices, deflt=self._user.get("notification_period"), ordered=True) html.help( _("Only during this time period the " "user will get notifications about host or service alerts.")) # Notification options notification_option_names = { # defined here: _() must be executed always! "host": { "d": _("Host goes down"), "u": _("Host gets unreachble"), "r": _("Host goes up again"), }, "service": { "w": _("Service goes into warning state"), "u": _("Service goes into unknown state"), "c": _("Service goes into critical state"), "r": _("Service recovers to OK"), }, "both": { "f": _("Start or end of flapping state"), "s": _("Start or end of a scheduled downtime"), } } forms.section(_("Notification Options")) for title, what, opts in [(_("Host events"), "host", "durfs"), (_("Service events"), "service", "wucrfs")]: html.write_text("%s:" % title) html.open_ul() user_opts = self._user.get(what + "_notification_options", opts) for opt in opts: opt_name = notification_option_names[what].get( opt, notification_option_names["both"].get(opt)) html.checkbox(what + "_" + opt, opt in user_opts, label=opt_name) html.br() html.close_ul() html.help( _("Here you specify which types of alerts " "will be notified to this contact. Note: these settings will only be saved " "and used if the user is member of a contact group.")) forms.section(_("Notification Method")) watolib.get_vs_flexible_notifications().render_input( "notification_method", self._user.get("notification_method")) else: forms.section(_("Fallback notifications"), simple=True) html.checkbox("fallback_contact", self._user.get("fallback_contact", False), label=_("Receive fallback notifications")) html.help( _("In case none of your notification rules handles a certain event a notification " "will be sent to this contact. This makes sure that in that case at least <i>someone</i> " "gets notified. Furthermore this contact will be used for notifications to any host or service " "that is not known to the monitoring. This can happen when you forward notifications " "from the Event Console.<br><br>Notification fallback can also configured in the global " "setting <a href=\"wato.py?mode=edit_configvar&varname=notification_fallback_email\">" "Fallback email address for notifications</a>.")) self._show_custom_user_attributes('notify') forms.header(_("Personal Settings"), isopen=False) select_language(self._user) self._show_custom_user_attributes('personal') # Later we could add custom macros here, which then could be used # for notifications. On the other hand, if we implement some check_mk # --notify, we could directly access the data in the account with the need # to store values in the monitoring core. We'll see what future brings. forms.end() html.button("save", _("Save")) if self._is_new_user: html.set_focus("user_id") else: html.set_focus("alias") html.hidden_fields() html.end_form()