def action(self): if not html.check_transaction(): return "users" if self._is_new_user: self._user_id = UserID(allow_empty=False).from_html_vars("user_id") user_attrs = {} else: self._user_id = html.get_unicode_input("edit").strip() user_attrs = self._users[self._user_id] # Full name user_attrs["alias"] = html.get_unicode_input("alias").strip() # Locking user_attrs["locked"] = html.get_checkbox("locked") increase_serial = False if self._user_id in self._users and self._users[ self._user_id]["locked"] != user_attrs["locked"] and user_attrs["locked"]: increase_serial = True # when user is being locked now, increase the auth serial # Authentication: Password or Secret auth_method = html.request.var("authmethod") if auth_method == "secret": secret = html.request.var("_auth_secret", "").strip() user_attrs["automation_secret"] = secret user_attrs["password"] = hash_password(secret) increase_serial = True # password changed, reflect in auth serial else: password = html.request.var("_password_" + self._pw_suffix(), '').strip() password2 = html.request.var("_password2_" + self._pw_suffix(), '').strip() # We compare both passwords only, if the user has supplied # the repeation! We are so nice to our power users... # Note: this validation is done before the main-validiation later on # It doesn't make any sense to put this block into the main validation function if password2 and password != password2: raise MKUserError("_password2", _("The both passwords do not match.")) # Detect switch back from automation to password if "automation_secret" in user_attrs: del user_attrs["automation_secret"] if "password" in user_attrs: del user_attrs["password"] # which was the encrypted automation password! if password: user_attrs["password"] = hash_password(password) user_attrs["last_pw_change"] = int(time.time()) increase_serial = True # password changed, reflect in auth serial # PW change enforcement user_attrs["enforce_pw_change"] = html.get_checkbox("enforce_pw_change") if user_attrs["enforce_pw_change"]: increase_serial = True # invalidate all existing user sessions, enforce relogon # Increase serial (if needed) if increase_serial: user_attrs["serial"] = user_attrs.get("serial", 0) + 1 # Email address user_attrs["email"] = EmailAddressUnicode().from_html_vars("email") idle_timeout = watolib.get_vs_user_idle_timeout().from_html_vars("idle_timeout") user_attrs["idle_timeout"] = idle_timeout if idle_timeout is not None: user_attrs["idle_timeout"] = idle_timeout elif idle_timeout is None and "idle_timeout" in user_attrs: del user_attrs["idle_timeout"] # Pager user_attrs["pager"] = html.request.var("pager", '').strip() if cmk.is_managed_edition(): customer = self._vs_customer.from_html_vars("customer") self._vs_customer.validate_value(customer, "customer") if customer != managed.default_customer_id(): user_attrs["customer"] = customer elif "customer" in user_attrs: del user_attrs["customer"] vs_sites = self._vs_sites() authorized_sites = vs_sites.from_html_vars("authorized_sites") vs_sites.validate_value(authorized_sites, "authorized_sites") if authorized_sites is not None: user_attrs["authorized_sites"] = authorized_sites elif "authorized_sites" in user_attrs: del user_attrs["authorized_sites"] # Roles user_attrs["roles"] = [ role for role in self._roles.keys() if html.get_checkbox("role_" + role) ] # Language configuration set_lang = html.get_checkbox("_set_lang") language = html.request.var("language") if set_lang: if language == "": language = None user_attrs["language"] = language elif not set_lang and "language" in user_attrs: del user_attrs["language"] # Contact groups cgs = [] for c in self._contact_groups: if html.get_checkbox("cg_" + c): cgs.append(c) user_attrs["contactgroups"] = cgs # Notification settings are only active if we do *not* have # rule based notifications! if not self._rbn_enabled(): # Notifications user_attrs["notifications_enabled"] = html.get_checkbox("notifications_enabled") ntp = html.request.var("notification_period") if ntp not in self._timeperiods: ntp = "24X7" user_attrs["notification_period"] = ntp for what, opts in [("host", "durfs"), ("service", "wucrfs")]: user_attrs[what + "_notification_options"] = "".join( [opt for opt in opts if html.get_checkbox(what + "_" + opt)]) value = watolib.get_vs_flexible_notifications().from_html_vars("notification_method") user_attrs["notification_method"] = value else: user_attrs["fallback_contact"] = html.get_checkbox("fallback_contact") # Custom user attributes for name, attr in userdb.get_user_attributes(): value = attr.valuespec().from_html_vars('ua_' + name) user_attrs[name] = value # Generate user "object" to update user_object = {self._user_id: {"attributes": user_attrs, "is_new_user": self._is_new_user}} # The following call validates and updated the users edit_users(user_object) return "users"
def _set(self, request): # NOTE: This encoding here should be kept # Otherwise and unicode encoded text will be written into the # configuration file with unknown side effects ruleset_name = request["ruleset_name"].encode("utf-8") # Future validation, currently the rule API actions are admin only, so the check is pointless # may_edit_ruleset(ruleset_name) # Check if configuration hash has changed in the meantime ruleset_dict = self._get_ruleset_configuration(ruleset_name) if "configuration_hash" in request: validate_config_hash(request["configuration_hash"], ruleset_dict) # Check permissions of new rules and rules we are going to delete new_ruleset = request["ruleset"] folders_set_ruleset = set(new_ruleset.keys()) folders_obsolete_ruleset = set( ruleset_dict.keys()) - folders_set_ruleset for check_folders in [folders_set_ruleset, folders_obsolete_ruleset]: for folder_path in check_folders: if not watolib.Folder.folder_exists(folder_path): raise MKUserError( None, _("Folder %s does not exist") % folder_path) rule_folder = watolib.Folder.folder(folder_path) rule_folder.need_permission("write") tag_to_group_map = ruleset_matcher.get_tag_to_group_map(config.tags) # Verify all rules rule_vs = watolib.Ruleset(ruleset_name, tag_to_group_map).rulespec.valuespec for folder_path, rules in new_ruleset.items(): for rule in rules: value = rule["value"] try: rule_vs.validate_datatype(value, "test_value") rule_vs.validate_value(value, "test_value") except MKException as e: # TODO: The abstract MKException should never be instanciated directly # Change this call site and make MKException an abstract base class raise MKException("ERROR: %s. Affected Rule %r" % (str(e), rule)) # Add new rulesets for folder_path, rules in new_ruleset.items(): folder = watolib.Folder.folder(folder_path) new_ruleset = watolib.Ruleset(ruleset_name, tag_to_group_map) new_ruleset.from_config(folder, rules) folder_rulesets = watolib.FolderRulesets(folder) folder_rulesets.load() # TODO: This add_change() call should be made by the data classes watolib.add_change("edit-ruleset", _("Set ruleset '%s' for '%s' with %d rules") % ( new_ruleset.title(), folder.title(), len(rules), ), sites=folder.all_site_ids()) folder_rulesets.set(ruleset_name, new_ruleset) folder_rulesets.save() # Remove obsolete rulesets for folder_path in folders_obsolete_ruleset: folder = watolib.Folder.folder(folder_path) folder_rulesets = watolib.FolderRulesets(folder) folder_rulesets.load() # TODO: This add_change() call should be made by the data classes watolib.add_change( "edit-ruleset", _("Deleted ruleset '%s' for '%s'") % ( watolib.Ruleset(ruleset_name, tag_to_group_map).title(), folder.title(), ), sites=folder.all_site_ids()) new_ruleset = watolib.Ruleset(ruleset_name, tag_to_group_map) new_ruleset.from_config(folder, []) folder_rulesets.set(ruleset_name, new_ruleset) folder_rulesets.save()
def _require_metric(value, varprefix): if value is None: raise MKUserError(varprefix, _("You need to select a metric"))
def action(self): # TODO: remove subclass specific things specifict things (everything with _type == 'user') if not html.check_transaction(): return title = html.request.get_unicode_input_mandatory("title").strip() if not title: raise MKUserError("title", _("Please specify a title.")) for this_attr in self._attrs: if title == this_attr['title'] and self._name != this_attr['name']: raise MKUserError( "alias", _("This alias is already used by the attribute %s.") % this_attr['name']) topic = html.request.get_unicode_input_mandatory('topic', '').strip() help_txt = html.request.get_unicode_input_mandatory('help', '').strip() show_in_table = html.get_checkbox('show_in_table') add_custom_macro = html.get_checkbox('add_custom_macro') if self._new: self._name = html.request.get_ascii_input_mandatory("name", '').strip() if not self._name: raise MKUserError( "name", _("Please specify a name for the new attribute.")) if ' ' in self._name: raise MKUserError( "name", _("Sorry, spaces are not allowed in attribute names.")) if not re.match("^[-a-z0-9A-Z_]*$", self._name): raise MKUserError( "name", _("Invalid attribute name. Only the characters a-z, A-Z, 0-9, _ and - are allowed." )) if [a for a in self._attrs if a['name'] == self._name]: raise MKUserError( "name", _("Sorry, there is already an attribute with that name.")) ty = html.request.get_ascii_input_mandatory('type', '').strip() if ty not in [t[0] for t in custom_attr_types()]: raise MKUserError('type', _('The choosen attribute type is invalid.')) self._attr = { 'name': self._name, 'type': ty, } self._attrs.append(self._attr) add_change( "edit-%sattr" % self._type, _("Create new %s attribute %s") % (self._type, self._name)) else: add_change( "edit-%sattr" % self._type, _("Modified %s attribute %s") % (self._type, self._name)) self._attr.update({ 'title': title, 'topic': topic, 'help': help_txt, 'show_in_table': show_in_table, 'add_custom_macro': add_custom_macro, }) self._add_extra_attrs_from_html_vars() save_custom_attrs_to_mk_file(self._all_attrs) self._update_config() return self._type + "_attrs"
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 _validate_value(self, value: DropdownChoiceValue, varprefix: str) -> None: if value is not None and not self._regex.match(ensure_str(value)): raise MKUserError(varprefix, self._regex_error)
def validate_url(cls, value: str, varprefix: str) -> None: if is_allowed_url(value, cross_domain=True, schemes=["http", "https"]): return raise MKUserError(varprefix, _("This URL ist not allowed to be used as bookmark"))
def validate_dynamic_levels(value, varprefix): if [v for v in value if v[0] < 0]: raise MKUserError( varprefix, _("You need to specify levels of at least 0 bytes."))
def action(self) -> ActionResult: if not transactions.check_transaction(): return redirect(self.mode_url()) if request.var("_delete"): delid = request.get_ascii_input_mandatory("_delete") if delid not in self._roles: raise MKUserError(None, _("This role does not exist.")) if transactions.transaction_valid() and self._roles[delid].get( 'builtin'): raise MKUserError(None, _("You cannot delete the builtin roles!")) users = userdb.load_users() for user in users.values(): if delid in user["roles"]: raise MKUserError( None, _("You cannot delete roles, that are still in use (%s)!" % delid)) self._rename_user_role(delid, None) # Remove from existing users del self._roles[delid] self._save_roles() watolib.add_change("edit-roles", _("Deleted role '%s'") % delid, sites=config.get_login_sites()) elif request.var("_clone"): cloneid = request.get_ascii_input_mandatory("_clone") try: cloned_role = self._roles[cloneid] except KeyError: raise MKUserError(None, _("This role does not exist.")) newid = cloneid while newid in self._roles: newid += "x" new_role = {} new_role.update(cloned_role) new_alias = new_role["alias"] while not watolib.is_alias_used("roles", newid, new_alias)[0]: new_alias += _(" (copy)") new_role["alias"] = new_alias if cloned_role.get("builtin"): new_role["builtin"] = False new_role["basedon"] = cloneid self._roles[newid] = new_role self._save_roles() watolib.add_change("edit-roles", _("Created new role '%s'") % newid, sites=config.get_login_sites()) return redirect(self.mode_url())
def _validate_alias(self, value, varprefix): unique, message = watolib.is_alias_used("timeperiods", self._name, value) if not unique: raise MKUserError("alias", message)
def do_log_ack(site, host_name, file_name): 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)) ack_msg = _('all logfiles on all hosts') 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)) ack_msg = _('all logfiles of host %s') % host_name 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))] ack_msg = _('the log file %s on host %s') % (file_name, host_name) 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 = _('log file %s on all hosts') % file_name html.header(_("Acknowledge %s") % html.render_text(ack_msg)) html.begin_context_buttons() button_all_logfiles() if host_name: html.context_button(_("All Logfiles of Host"), html.makeuri([('file', '')])) if host_name and file_name: html.context_button(_("Back to Logfile"), html.makeuri([])) html.end_context_buttons() ack = html.request.var('_ack') if not html.confirm( _("Do you really want to acknowledge %s by <b>deleting</b> all stored messages?") % ack_msg): html.footer() return if not config.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 _validate_id(self, value, varprefix): if value in self._timeperiods: raise MKUserError( varprefix, _("This name is already being used by another timeperiod."))
def _get_timeperiod(self, name): try: return self._timeperiods[name] except KeyError: raise MKUserError(None, _("This timeperiod does not exist."))
def edit_dictionaries( dictionaries: 'Sequence[Tuple[str, Union[Transform, Dictionary]]]', value: Dict[str, Any], focus: Optional[str] = None, hover_help: bool = True, validate: Optional[Callable[[Any], None]] = None, title: Optional[str] = None, method: str = "GET", preview: bool = False, varprefix: str = "", formname: str = "form", consume_transid: bool = True): if request.get_ascii_input( "filled_in") == formname and transactions.transaction_valid(): if not preview and consume_transid: transactions.check_transaction() messages: List[str] = [] new_value: Dict[str, Dict[str, Any]] = {} for keyname, vs_dict in dictionaries: dict_varprefix = varprefix + keyname new_value[keyname] = {} try: edited_value = vs_dict.from_html_vars(dict_varprefix) vs_dict.validate_value(edited_value, dict_varprefix) new_value[keyname].update(edited_value) except MKUserError as e: messages.append("%s: %s" % (vs_dict.title() or _("Properties"), e)) user_errors.add(e) except Exception as e: messages.append("%s: %s" % (vs_dict.title() or _("Properties"), e)) user_errors.add(MKUserError(None, str(e))) if validate and not user_errors: try: validate(new_value[keyname]) except MKUserError as e: messages.append(str(e)) user_errors.add(e) if messages: messages_joined = "".join(["%s<br>\n" % m for m in messages]) if not preview: html.show_error(messages_joined) else: raise MKUserError(None, messages_joined) else: return new_value html.begin_form(formname, method=method) for keyname, vs_dict in dictionaries: dict_varprefix = varprefix + keyname subvalue = value.get(keyname, {}) vs_dict.render_input_as_form(dict_varprefix, subvalue) end() # Should be ignored be hidden_fields, but I do not dare to change it there request.del_var("filled_in") html.hidden_fields() html.end_form()
def action(self) -> ActionResult: # TODO: remove subclass specific things specifict things (everything with _type == 'user') if not transactions.check_transaction(): return None title = request.get_unicode_input_mandatory("title").strip() if not title: raise MKUserError("title", _("Please specify a title.")) for this_attr in self._attrs: if title == this_attr["title"] and self._name != this_attr["name"]: raise MKUserError( "alias", _("This alias is already used by the attribute %s.") % this_attr["name"], ) topic = request.get_unicode_input_mandatory("topic", "").strip() help_txt = request.get_unicode_input_mandatory("help", "").strip() show_in_table = html.get_checkbox("show_in_table") add_custom_macro = html.get_checkbox("add_custom_macro") if self._new: self._name = request.get_ascii_input_mandatory("name", "").strip() if not self._name: raise MKUserError( "name", _("Please specify a name for the new attribute.")) if " " in self._name: raise MKUserError( "name", _("Sorry, spaces are not allowed in attribute names.")) if not re.match("^[-a-z0-9A-Z_]*$", self._name): raise MKUserError( "name", _("Invalid attribute name. Only the characters a-z, A-Z, 0-9, _ and - are allowed." ), ) if [a for a in self._attrs if a["name"] == self._name]: raise MKUserError( "name", _("Sorry, there is already an attribute with that name.")) ty = request.get_ascii_input_mandatory("type", "").strip() if ty not in [t[0] for t in custom_attr_types()]: raise MKUserError("type", _("The choosen attribute type is invalid.")) self._attr = { "name": self._name, "type": ty, } self._attrs.append(self._attr) add_change( "edit-%sattr" % self._type, _("Create new %s attribute %s") % (self._type, self._name), ) else: add_change( "edit-%sattr" % self._type, _("Modified %s attribute %s") % (self._type, self._name)) self._attr.update({ "title": title, "topic": topic, "help": help_txt, "show_in_table": show_in_table, "add_custom_macro": add_custom_macro, }) self._add_extra_attrs_from_html_vars() save_custom_attrs_to_mk_file(self._all_attrs) self._update_config() return redirect(mode_url(self._type + "_attrs"))
def action(self) -> ActionResult: if html.form_submitted("search"): return None alias = request.get_unicode_input("alias") unique, info = watolib.is_alias_used("roles", self._role_id, alias) if not unique: raise MKUserError("alias", info) new_id = request.get_ascii_input_mandatory("id") if not new_id: raise MKUserError("id", "You have to provide a ID.") if not re.match("^[-a-z0-9A-Z_]*$", new_id): raise MKUserError( "id", _("Invalid role ID. Only the characters a-z, A-Z, 0-9, _ and - are allowed." )) if new_id != self._role_id: if new_id in self._roles: raise MKUserError("id", _("The ID is already used by another role")) self._role["alias"] = alias # based on if not self._role.get("builtin"): basedon = request.get_ascii_input_mandatory("basedon") if basedon not in config.builtin_role_ids: raise MKUserError( "basedon", _("Invalid valid for based on. Must be id of builtin rule." )) self._role["basedon"] = basedon # Permissions permissions = self._role["permissions"] for var_name, value in request.itervars(prefix="perm_"): try: perm = permission_registry[var_name[5:]] except KeyError: continue if value == "yes": permissions[perm.name] = True elif value == "no": permissions[perm.name] = False elif value == "default": try: del permissions[perm.name] except KeyError: pass # Already at defaults if self._role_id != new_id: self._roles[new_id] = self._role del self._roles[self._role_id] self._rename_user_role(self._role_id, new_id) self._save_roles() watolib.add_change("edit-roles", _("Modified user role '%s'") % new_id, sites=config.get_login_sites()) return redirect(mode_url("roles"))
def _reload_js(self): # Be compatible to pre 1.5.0i2 format # TODO: Do this conversion during __init__() or during config loading if "graph_render_options" not in self._dashlet_spec: if self._dashlet_spec.pop("show_service", True): title_format = ("add_title_infos", ["add_host_name", "add_service_description"]) else: title_format = ("plain", []) self._dashlet_spec["graph_render_options"] = { "show_legend": self._dashlet_spec.pop("show_legend", False), "title_format": title_format, } host = self._dashlet_spec['context'].get('host', html.request.var("host")) if not host: raise MKUserError('host', _('Missing needed host parameter.')) service = self._dashlet_spec['context'].get('service') if not service: service = "_HOST_" # When the site is available via URL context, use it. Otherwise it is needed # to check all sites for the requested host if html.request.has_var('site'): site = html.request.var('site') else: query = "GET hosts\nFilter: name = %s\nColumns: name" % livestatus.lqencode( host) try: sites.live().set_prepend_site(True) site = sites.live().query_column(query)[0] except IndexError: raise MKUserError( "host", _("The host could not be found on any active site.")) finally: sites.live().set_prepend_site(False) # New graphs which have been added via "add to visual" option don't have a timerange # configured. So we assume the default timerange here by default. timerange = self._dashlet_spec.get('timerange', '1') graph_identification = ("template", { "site": site, "host_name": host, "service_description": service, "graph_index": self._dashlet_spec["source"] - 1, }) graph_render_options = self._dashlet_spec["graph_render_options"] return "dashboard_render_graph(%d, %s, %s, '%s')" % \ (self._dashlet_id, json.dumps(graph_identification), json.dumps(graph_render_options), timerange)
def edit_dictionaries( dictionaries, # type: List[Tuple[str, Union[Transform, Dictionary]]] value, # type: Dict[str, Any] focus=None, # type: Optional[str] hover_help=True, # type: bool validate=None, # type: Optional[Callable[[Any], None]] buttontext=None, # type: Optional[str] title=None, # type: Optional[str] buttons=None, # type: List[Tuple[str, str, str]] method="GET", # type: str preview=False, # type: bool varprefix="", # type: str formname="form", # type: str consume_transid=True # type: bool ): if html.request.get_ascii_input( "filled_in") == formname and html.transaction_valid(): if not preview and consume_transid: html.check_transaction() messages = [] # type: List[str] new_value = {} # type: Dict[str, Dict[str, Any]] for keyname, vs_dict in dictionaries: dict_varprefix = varprefix + keyname new_value[keyname] = {} try: edited_value = vs_dict.from_html_vars(dict_varprefix) vs_dict.validate_value(edited_value, dict_varprefix) new_value[keyname].update(edited_value) except MKUserError as e: messages.append("%s: %s" % (vs_dict.title() or _("Properties"), e)) html.add_user_error(e.varname, e) except Exception as e: messages.append("%s: %s" % (vs_dict.title() or _("Properties"), e)) html.add_user_error(None, e) if validate and not html.has_user_errors(): try: validate(new_value[keyname]) except MKUserError as e: messages.append("%s" % e) html.add_user_error(e.varname, e) if messages: messages_joined = "".join(["%s<br>\n" % m for m in messages]) if not preview: html.show_error(messages_joined) else: raise MKUserError(None, messages_joined) else: return new_value html.begin_form(formname, method=method) for keyname, vs_dict in dictionaries: dict_varprefix = varprefix + keyname subvalue = value.get(keyname, {}) vs_dict.render_input_as_form(dict_varprefix, subvalue) end() if buttons: for name, button_title, _icon in buttons: html.button(name, button_title) else: if buttontext is None: buttontext = _("Save") html.button("save", buttontext) # Should be ignored be hidden_fields, but I do not dare to change it there html.request.del_var("filled_in") html.hidden_fields() html.end_form()
def decrypt_private_key(encrypted_private_key, passphrase): try: return crypto.load_privatekey(crypto.FILETYPE_PEM, encrypted_private_key, passphrase.encode("utf-8")) except crypto.Error: raise MKUserError("key_p_passphrase", _("Invalid pass phrase"))
def validate_configuration(cls, site_id, site_configuration, all_sites): if not re.match("^[-a-z0-9A-Z_]+$", site_id): raise MKUserError( "id", _("The site id must consist only of letters, digit and the underscore." )) if not site_configuration.get("alias"): raise MKUserError( "alias", _("Please enter an alias name or description for the site %s.") % site_id) if site_configuration.get("url_prefix") and site_configuration.get( "url_prefix")[-1] != "/": raise MKUserError("url_prefix", _("The URL prefix must end with a slash.")) # Connection if site_configuration["socket"][ 0] == "local" and site_id != config.omd_site(): raise MKUserError( "method_sel", _("You can only configure a local site connection for " "the local site. The site IDs ('%s' and '%s') are " "not equal.") % (site_id, config.omd_site())) # Timeout if "timeout" in site_configuration: timeout = site_configuration["timeout"] try: int(timeout) except ValueError: raise MKUserError( "timeout", _("The timeout %s is not a valid integer number.") % timeout) # Status host status_host = site_configuration.get("status_host") if status_host: status_host_site, status_host_name = status_host if status_host_site not in all_sites: raise MKUserError( "sh_site", _("The site of the status host does not exist.")) if status_host_site == site_id: raise MKUserError( "sh_site", _("You cannot use the site itself as site of the status host." )) if not status_host_name: raise MKUserError( "sh_host", _("Please specify the name of the status host.")) if site_configuration.get("replication"): multisiteurl = site_configuration.get("multisiteurl") if not site_configuration.get("multisiteurl"): raise MKUserError( "multisiteurl", _("Please enter the Multisite URL of the slave site.")) if not multisiteurl.endswith("/check_mk/"): raise MKUserError( "multisiteurl", _("The Multisite URL must end with /check_mk/")) if not multisiteurl.startswith("http://") and \ not multisiteurl.startswith("https://"): raise MKUserError( "multisiteurl", _("The Multisites URL must begin with <tt>http://</tt> or <tt>https://</tt>." )) if site_configuration["socket"][0] == "local": raise MKUserError( "replication", _("You cannot do replication with the local site.")) # User synchronization user_sync_valuespec = cls.user_sync_valuespec() user_sync_valuespec.validate_value(site_configuration.get("user_sync"), "user_sync")
def action(self): if html.request.var("_search"): # just commit to search form return ### Operations on SUBFOLDERS if html.request.var("_delete_folder"): if html.transaction_valid(): return self._delete_subfolder_after_confirm( html.request.var("_delete_folder")) return elif html.request.has_var("_move_folder_to"): if html.check_transaction(): what_folder = watolib.Folder.folder(html.request.var("_ident")) target_folder = watolib.Folder.folder( html.request.var("_move_folder_to")) watolib.Folder.current().move_subfolder_to( what_folder, target_folder) return ### Operations on HOSTS # Deletion of single hosts delname = html.request.var("_delete_host") if delname and watolib.Folder.current().has_host(delname): return delete_host_after_confirm(delname) # Move single hosts to other folders if html.request.has_var("_move_host_to"): hostname = html.request.var("_ident") if hostname: target_folder = watolib.Folder.folder( html.request.var("_move_host_to")) watolib.Folder.current().move_hosts([hostname], target_folder) return # bulk operation on hosts if not html.transaction_valid(): return # Host table: No error message on search filter reset if html.request.var("_hosts_reset_sorting") or html.request.var( "_hosts_sort"): return selected_host_names = get_hostnames_from_checkboxes() if len(selected_host_names) == 0: raise MKUserError( None, _("Please select some hosts before doing bulk operations on hosts." )) if html.request.var("_bulk_inventory"): return "bulkinventory" elif html.request.var("_parentscan"): return "parentscan" # Deletion if html.request.var("_bulk_delete"): return self._delete_hosts_after_confirm(selected_host_names) # Move elif html.request.var("_bulk_move"): target_folder_path = html.request.var( "bulk_moveto", html.request.var("_top_bulk_moveto")) if target_folder_path == "@": raise MKUserError("bulk_moveto", _("Please select the destination folder")) target_folder = watolib.Folder.folder(target_folder_path) watolib.Folder.current().move_hosts(selected_host_names, target_folder) return None, _("Moved %d hosts to %s") % (len(selected_host_names), target_folder.title()) # Move to target folder (from import) elif html.request.var("_bulk_movetotarget"): return self._move_to_imported_folders(selected_host_names) elif html.request.var("_bulk_edit"): return "bulkedit" elif html.request.var("_bulk_cleanup"): return "bulkcleanup"
def validate_input(self, value, varprefix): if not isinstance(value, dict): raise MKUserError(self.name(), "Unknown format.") self.load_data() self._vs_contactgroups().validate_value(value.get("groups", []), varprefix)
def validate_process_discovery_descr_option(description, varprefix): if '%s' in description and re.search(r'%(\d+)', description): raise MKUserError( varprefix, _('Combining "%s" and "%1" style replacements in the sevice description is not allowed.' ))
def _action(self) -> None: assert config.user.id is not None users = userdb.load_users(lock=True) user = users[config.user.id] cur_password = html.request.get_str_input_mandatory('cur_password') password = html.request.get_str_input_mandatory('password') password2 = html.request.get_str_input_mandatory('password2', '') # 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 userdb.check_credentials(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) user['password'] = hash_password(password) user['last_pw_change'] = int(time.time()) # In case the user was enforced to change it's password, remove the flag try: del user['enforce_pw_change'] except KeyError: pass # Increase serial to invalidate old authentication cookies if 'serial' not in user: user['serial'] = 1 else: user['serial'] += 1 userdb.save_users(users) # Set the new cookie to prevent logout for the current user login.update_auth_cookie(config.user.id) # 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. origtarget = html.request.get_str_input_mandatory( '_origtarget', 'user_change_pw.py') if config.user.authorized_login_sites(): raise HTTPRedirect( makeuri_contextless(request, [("back", origtarget)], filename="user_profile_replicate.py")) raise HTTPRedirect(origtarget)
def _discover_services(self, request): mode = request.get("mode", "new") hostname = request.get("hostname") check_hostname(hostname, should_exist=True) host = watolib.Host.host(hostname) host_attributes = host.effective_attributes() if host.is_cluster(): # This is currently the only way to get some actual discovery statitics. # Start a dry-run -> Get statistics # Do an actual discovery on the nodes -> data is written result = watolib.check_mk_automation(host_attributes.get("site"), "try-inventory", ["@scan"] + [hostname]) counts = {"new": 0, "old": 0} for entry in result["check_table"]: if entry[0] in counts: counts[entry[0]] += 1 counts = { hostname: ( counts["new"], 0, # this info is not available for clusters counts["old"], counts["new"] + counts["old"]) } # A cluster cannot fail, just the nodes. This information is currently discarded failed_hosts = None watolib.check_mk_automation(host_attributes.get("site"), "inventory", ["@scan", mode] + host.cluster_nodes()) else: counts, failed_hosts = watolib.check_mk_automation( host_attributes.get("site"), "inventory", ["@scan", mode] + [hostname]) if failed_hosts: if not host.discovery_failed(): host.set_discovery_failed() raise MKUserError( None, _("Failed to inventorize %s: %s") % (hostname, failed_hosts[hostname])) if host.discovery_failed(): host.clear_discovery_failed() if mode == "refresh": message = _( "Refreshed check configuration of host [%s] with %d services" ) % (hostname, counts[hostname][3]) watolib.add_service_change(host, "refresh-autochecks", message) else: message = _( "Saved check configuration of host [%s] with %d services") % ( hostname, counts[hostname][3]) watolib.add_service_change(host, "set-autochecks", message) msg = _( "Service discovery successful. Added %d, removed %d, kept %d, total %d services " "and %d new, %d total host labels") % tuple(counts[hostname]) return msg
def action(self): if not html.check_transaction(): return "tags" vs = self._valuespec() tag_group_spec = vs.from_html_vars("tag_group") vs.validate_value(tag_group_spec, "tag_group") # Create new object with existing host tags changed_hosttags_config = cmk.utils.tags.TagConfig() changed_hosttags_config.parse_config( self._tag_config_file.load_for_modification()) changed_tag_group = cmk.utils.tags.TagGroup(tag_group_spec) self._tag_group = changed_tag_group if self._new: # Inserts and verifies changed tag group changed_hosttags_config.insert_tag_group(changed_tag_group) try: changed_hosttags_config.validate_config() except MKGeneralException as e: raise MKUserError(None, "%s" % e) self._save_tags_and_update_hosts( changed_hosttags_config.get_dict_format()) add_change( "edit-hosttags", _("Created new host tag group '%s'") % changed_tag_group.id) return "tags", _( "Created new host tag group '%s'") % changed_tag_group.title # Updates and verifies changed tag group changed_hosttags_config.update_tag_group(changed_tag_group) try: changed_hosttags_config.validate_config() except MKGeneralException as e: raise MKUserError(None, "%s" % e) remove_tag_ids, replace_tag_ids = [], {} new_by_title = {tag.title: tag.id for tag in changed_tag_group.tags} for former_tag in self._untainted_tag_group.tags: # Detect renaming if former_tag.title in new_by_title: new_id = new_by_title[former_tag.title] if new_id != former_tag.id: # new_id may be None replace_tag_ids[former_tag.id] = new_id continue # Detect removal if former_tag.id is not None \ and former_tag.id not in [ tmp_tag.id for tmp_tag in changed_tag_group.tags ]: # remove explicit tag (hosts/folders) or remove it from tag specs (rules) remove_tag_ids.append(former_tag.id) tg_id = self._tag_group.id if tg_id is None: raise Exception("tag group ID not set") operation = OperationReplaceGroupedTags(tg_id, remove_tag_ids, replace_tag_ids) # Now check, if any folders, hosts or rules are affected message = _rename_tags_after_confirmation(operation) if message: self._save_tags_and_update_hosts( changed_hosttags_config.get_dict_format()) add_change( "edit-hosttags", _("Edited host tag group %s (%s)") % (message, self._id)) return "tags", message is not True and message or None return "tags"
def _get_group(self, group_name): try: return self._groups[self._name] except KeyError: raise MKUserError(None, _("This group does not exist."))
def _rename_tags_after_confirmation(operation): # type: (ABCOperation) -> Union[bool, str] """Handle renaming and deletion of tags Find affected hosts, folders and rules. Remove or fix those rules according the users' wishes. """ repair_mode = html.request.var("_repair") if repair_mode is not None: try: mode = TagCleanupMode(repair_mode) except ValueError: raise MKUserError("_repair", "Invalid mode") if mode == TagCleanupMode.ABORT: raise MKUserError("id_0", _("Aborting change.")) # make attribute unknown to system, important for save() operations if isinstance(operation, OperationRemoveTagGroup): watolib.host_attributes.undeclare_host_tag_attribute( operation.tag_group_id) affected_folders, affected_hosts, affected_rulesets = \ _change_host_tags_in_folders(operation, mode, watolib.Folder.root_folder()) return _("Modified folders: %d, modified hosts: %d, modified rulesets: %d") % \ (len(affected_folders), len(affected_hosts), len(affected_rulesets)) message = u"" affected_folders, affected_hosts, affected_rulesets = \ _change_host_tags_in_folders(operation, TagCleanupMode.CHECK, watolib.Folder.root_folder()) if affected_folders: message += _("Affected folders with an explicit reference to this tag " "group and that are affected by the change") + ":" with html.plugged(): _show_affected_folders(affected_folders) message += html.drain() if affected_hosts: message += _("Hosts where this tag group is explicitely set " "and that are effected by the change") + ":" with html.plugged(): _show_affected_hosts(affected_hosts) message += html.drain() if affected_rulesets: message += _( "Rulesets that contain rules with references to the changed tags" ) + ":" with html.plugged(): _show_affected_rulesets(affected_rulesets) message += html.drain() if message: wato_html_head(operation.confirm_title()) html.open_div(class_="really") html.h3(_("Your modifications affect some objects")) html.write_text(message) html.br() html.write_text( _("WATO can repair things for you. It can rename tags in folders, host and rules. " "Removed tag groups will be removed from hosts and folders, removed tags will be " "replaced with the default value for the tag group (for hosts and folders). What " "rules concern, you have to decide how to proceed.")) html.begin_form("confirm", method="POST") if affected_rulesets and _is_removing_tags(operation): html.br() html.b( _("Some tags that are used in rules have been removed by you. What " "shall we do with that rules?")) html.open_ul() html.radiobutton( "_repair", "remove", True, _("Just remove the affected tags from the rules.")) html.br() html.radiobutton( "_repair", "delete", False, _("Delete rules containing tags that have been removed, if tag is used in a positive sense. Just remove that tag if it's used negated." )) else: html.open_ul() html.radiobutton("_repair", "repair", True, _("Fix affected folders, hosts and rules.")) html.br() html.radiobutton("_repair", "abort", False, _("Abort your modifications.")) html.close_ul() html.button("_do_confirm", _("Proceed"), "") html.hidden_fields(add_action_vars=True) html.end_form() html.close_div() return False return True
def _validate_value(self, value: DropdownChoiceModel, varprefix: str) -> None: if not value or value == self._MARKER_DEPRECATED_CHOICE: raise MKUserError(varprefix, _("Please select a graph."))
def _show_service_info(self, all_rulesets): assert self._service is not None serviceinfo = watolib.check_mk_automation( self._host.site_id(), "analyse-service", [self._hostname, self._service]) if not serviceinfo: return forms.header(_("Check origin and parameters"), isopen=True, narrow=True, css="rulesettings") origin = serviceinfo["origin"] origin_txt = { "active": _("Active check"), "static": _("Manual check"), "auto": _("Inventorized check"), "classic": _("Classical check"), }[origin] self._render_rule_reason(_("Type of check"), None, "", "", False, origin_txt) # First case: discovered checks. They come from var/check_mk/autochecks/HOST. if origin == "auto": checkgroup = serviceinfo["checkgroup"] checktype = serviceinfo["checktype"] if not checkgroup: self._render_rule_reason( _("Parameters"), None, "", "", True, _("This check is not configurable via WATO")) # Logwatch needs a special handling, since it is not configured # via checkgroup_parameters but via "logwatch_rules" in a special # WATO module. elif checkgroup == "logwatch": rulespec = rulespec_registry["logwatch_rules"] self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=serviceinfo["item"], svc_desc=self._service, known_settings=serviceinfo["parameters"]) else: # Note: some discovered checks have a check group but # *no* ruleset for discovered checks. One example is "ps". # That can be configured as a manual check or created by # inventory. But in the later case all parameters are set # by the inventory. This will be changed in a later version, # but we need to address it anyway. grouprule = "checkgroup_parameters:" + checkgroup if grouprule not in rulespec_registry: try: rulespec = rulespec_registry["static_checks:" + checkgroup] except KeyError: self._render_rule_reason( _("Parameters"), None, "", "", True, _("This check is not configurable via WATO")) return url = watolib.folder_preserving_link([ ('mode', 'edit_ruleset'), ('varname', "static_checks:" + checkgroup), ('host', self._hostname) ]) assert isinstance(rulespec.valuespec, Tuple) self._render_rule_reason( _("Parameters"), url, _("Determined by discovery"), None, False, rulespec.valuespec._elements[2].value_to_text( serviceinfo["parameters"])) else: rulespec = rulespec_registry[grouprule] self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=serviceinfo["item"], svc_desc=self._service, known_settings=serviceinfo["parameters"]) elif origin == "static": checkgroup = serviceinfo["checkgroup"] checktype = serviceinfo["checktype"] if not checkgroup: html.write_text(_("This check is not configurable via WATO")) else: rulespec = rulespec_registry["static_checks:" + checkgroup] itemspec = rulespec.item_spec if itemspec: item_text = itemspec.value_to_text(serviceinfo["item"]) assert rulespec.item_spec is not None title = rulespec.item_spec.title() else: item_text = serviceinfo["item"] title = _("Item") self._render_rule_reason(title, None, "", "", False, item_text) self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=serviceinfo["item"], svc_desc=self._service, known_settings=self._PARAMETERS_OMIT) assert isinstance(rulespec.valuespec, Tuple) html.write(rulespec.valuespec._elements[2].value_to_text( serviceinfo["parameters"])) html.close_td() html.close_tr() html.close_table() elif origin == "active": checktype = serviceinfo["checktype"] rulespec = rulespec_registry["active_checks:" + checktype] self._output_analysed_ruleset( all_rulesets, rulespec, svc_desc_or_item=None, svc_desc=None, known_settings=serviceinfo["parameters"]) elif origin == "classic": ruleset = all_rulesets.get("custom_checks") origin_rule_result = self._get_custom_check_origin_rule( ruleset, self._hostname, self._service) if origin_rule_result is None: raise MKUserError( None, _("Failed to determine origin rule of %s / %s") % (self._hostname, self._service)) rule_folder, rule_index, rule = origin_rule_result url = watolib.folder_preserving_link([('mode', 'edit_ruleset'), ('varname', "custom_checks"), ('host', self._hostname)]) forms.section(html.render_a(_("Command Line"), href=url)) url = watolib.folder_preserving_link([('mode', 'edit_rule'), ('varname', "custom_checks"), ('rule_folder', rule_folder.path()), ('rule_id', rule.id), ('host', self._hostname)]) html.open_table(class_="setting") html.open_tr() html.open_td(class_="reason") html.a("%s %d %s %s" % (_("Rule"), rule_index + 1, _("in"), rule_folder.title()), href=url) html.close_td() html.open_td(class_=["settingvalue", "used"]) if "command_line" in serviceinfo: html.tt(serviceinfo["command_line"]) else: html.write_text(_("(no command line, passive check)")) html.close_td() html.close_tr() html.close_table() self._show_labels(serviceinfo.get("labels", {}), "service", serviceinfo.get("label_sources", {}))