def _get_timeperiod(self, name): try: return self._timeperiods[name] except KeyError: raise MKUserError(None, _("This timeperiod does not exist."))
def page_list(cls): cls.load() # custom_columns = [] # render_custom_buttons = None # render_custom_columns = None # render_custom_context_buttons = None # check_deletable_handler = None cls.need_overriding_permission("edit") html.header(cls.phrase("title_plural")) html.begin_context_buttons() html.context_button(cls.phrase("new"), cls.create_url(), "new_" + cls.type_name()) # TODO: Remove this legacy code as soon as views, dashboards and reports have been # moved to pagetypes.py html.context_button(_("Views"), "edit_views.py", "view") html.context_button(_("Dashboards"), "edit_dashboards.py", "dashboard") def has_reporting(): try: # The suppression below is OK, we just want to check if the module is there. import cmk.gui.cee.reporting as _dummy # noqa: F401 # pylint: disable=import-outside-toplevel return True except ImportError: return False if has_reporting(): html.context_button(_("Reports"), "edit_reports.py", "report") # ## if render_custom_context_buttons: # ## render_custom_context_buttons() for other_type_name, other_pagetype in page_types.items(): if cls.type_name() != other_type_name: html.context_button( other_pagetype.phrase("title_plural").title(), '%ss.py' % other_type_name, other_type_name) html.end_context_buttons() # Deletion delname = html.request.var("_delete") if delname and html.transaction_valid(): owner = UserId(html.request.get_unicode_input_mandatory('_owner', config.user.id)) try: instance = cls.instance((owner, delname)) except KeyError: raise MKUserError( "_delete", _("The %s you are trying to delete " "does not exist.") % cls.phrase("title")) if not instance.may_delete(): raise MKUserError("_delete", _("You are not permitted to perform this action.")) try: if owner != config.user.id: owned_by = _(" (owned by %s)") % owner else: owned_by = "" c = html.confirm( _("Please confirm the deletion of \"%s\"%s.") % (instance.title(), owned_by)) if c: cls.remove_instance((owner, delname)) cls.save_user_instances(owner) html.reload_sidebar() elif c is False: html.footer() return except MKUserError as e: html.user_error(e) # Bulk delete if html.request.var("_bulk_delete_my") and html.transaction_valid(): if cls._bulk_delete_after_confirm("my") is False: html.footer() return elif html.request.var("_bulk_delete_foreign") and html.transaction_valid(): if cls._bulk_delete_after_confirm("foreign") is False: html.footer() return my_instances, foreign_instances, builtin_instances = cls.get_instances() for what, title, instances in [ ("my", _('Customized'), my_instances), ("foreign", _('Owned by other users'), foreign_instances), ("builtin", _('Builtin'), builtin_instances), ]: if not instances: continue html.open_h3() html.write(title) html.close_h3() if what != "builtin": html.begin_form("bulk_delete_%s" % what, method="POST") with table_element(limit=None) as table: for instance in instances: table.row() if what != "builtin" and instance.may_delete(): table.cell(html.render_input( "_toggle_group", type_="button", class_="checkgroup", onclick="cmk.selection.toggle_all_rows(this.form);", value='X'), sortable=False, css="checkbox") html.checkbox("_c_%s+%s+%s" % (what, instance.owner(), instance.name())) # Actions table.cell(_('Actions'), css='buttons visuals') # View if isinstance(instance, PageRenderer): html.icon_button(instance.page_url(), _("View"), "new_" + cls.type_name()) # Clone / Customize html.icon_button(instance.clone_url(), _("Create a customized copy of this"), "clone") # Delete if instance.may_delete(): html.icon_button(instance.delete_url(), _("Delete!"), "delete") # Edit if instance.may_edit(): html.icon_button(instance.edit_url(), _("Edit"), "edit") cls.custom_list_buttons(instance) # Internal ID of instance (we call that 'name') table.cell(_('ID'), instance.name(), css="narrow") # Title table.cell(_('Title')) html.write_text(instance.render_title()) html.help(_u(instance.description())) # Custom columns specific to that page type instance.render_extra_columns(table) # ## for title, renderer in custom_columns: # ## table.cell(title, renderer(visual)) # Owner if instance.is_builtin(): ownertxt = html.render_i(_("builtin")) else: ownertxt = instance.owner() table.cell(_('Owner'), ownertxt) table.cell(_('Public'), _("yes") if instance.is_public() else _("no")) table.cell(_('Hidden'), _("yes") if instance.is_hidden() else _("no")) # FIXME: WTF?!? # TODO: Haeeh? Another custom columns # ## if render_custom_columns: # ## render_custom_columns(visual_name, visual) if what != "builtin": html.button("_bulk_delete_%s" % what, _("Bulk delete"), "submit", style="margin-top:10px") html.hidden_fields() html.end_form() html.footer() return
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 action(self, cmdtag, spec, row, row_index, num_rows): down_from = int(time.time()) down_to = None if self._has_recurring_downtimes() and html.get_checkbox("_down_do_recur"): from cmk.gui.cee.plugins.wato.cmc import recurring_downtimes_types # pylint: disable=no-name-in-module recurring_type = int(html.request.var("_down_recurring")) title_start = _("schedule a periodic downtime every %s") % recurring_downtimes_types( )[recurring_type] else: title_start = _("schedule an immediate downtime") rangebtns = (varname for varname, _value in html.request.itervars(prefix="_downrange")) def resolve_end(name): now = time.localtime(down_from) if name == "next_day": return time.mktime((now.tm_year, now.tm_mon, now.tm_mday, 23, 59, 59, 0, 0, now.tm_isdst)) + 1, \ _("<b>%s until 24:00:00</b> on") % title_start elif name == "next_week": wday = now.tm_wday days_plus = 6 - wday res = time.mktime( (now.tm_year, now.tm_mon, now.tm_mday, 23, 59, 59, 0, 0, now.tm_isdst)) + 1 res += days_plus * 24 * 3600 return res, _("<b>%s until sunday night</b> on") % title_start elif name == "next_month": new_month = now.tm_mon + 1 if new_month == 13: new_year = now.tm_year + 1 new_month = 1 else: new_year = now.tm_year return time.mktime((new_year, new_month, 1, 0, 0, 0, 0, 0, now.tm_isdst)), \ _("<b>%s until end of month</b> on") % title_start elif name == "next_year": return time.mktime((now.tm_year, 12, 31, 23, 59, 59, 0, 0, now.tm_isdst)) + 1, \ _("<b>%s until end of %d</b> on") % (title_start, now.tm_year) else: duration = int(name) return down_from + duration, \ _("<b>%s of %s length</b> on") %\ (title_start, self._get_duration_human_readable(duration)) try: rangebtn = next(rangebtns) except StopIteration: rangebtn = None if rangebtn: _btnname, end = rangebtn.split("__", 1) down_to, title = resolve_end(end) elif html.request.var("_down_from_now"): try: minutes = int(html.request.var("_down_minutes", "")) except ValueError: minutes = 0 if minutes <= 0: raise MKUserError("_down_minutes", _("Please enter a positive number of minutes.")) down_to = time.time() + minutes * 60 title = _("<b>%s for the next %d minutes</b> on") % (title_start, minutes) elif html.request.var("_down_adhoc"): minutes = config.adhoc_downtime.get("duration", 0) down_to = time.time() + minutes * 60 title = _("<b>%s for the next %d minutes</b> on") % (title_start, minutes) elif html.request.var("_down_custom"): down_from = html.get_datetime_input("_down_from") down_to = html.get_datetime_input("_down_to") if down_to < time.time(): raise MKUserError( "_down_to", _("You cannot set a downtime that ends in the past. " "This incident will be reported.")) if down_to < down_from: raise MKUserError("_down_to", _("Your end date is before your start date.")) title = _("<b>schedule a downtime from %s to %s</b> on ") % (time.asctime( time.localtime(down_from)), time.asctime(time.localtime(down_to))) elif html.request.var("_down_remove") and config.user.may("action.remove_all_downtimes"): if html.request.var("_on_hosts"): raise MKUserError( "_on_hosts", _("The checkbox for setting host downtimes does not work when removing downtimes." )) downtime_ids = [] if cmdtag == "HOST": prefix = "host_" else: prefix = "service_" for id_ in row[prefix + "downtimes"]: if id_ != "": downtime_ids.append(int(id_)) commands = [] for dtid in downtime_ids: commands.append("DEL_%s_DOWNTIME;%d\n" % (cmdtag, dtid)) title = _("<b>remove all scheduled downtimes</b> of ") return commands, title if down_to: if html.request.var("_down_adhoc"): comment = config.adhoc_downtime.get("comment", "") else: comment = html.get_unicode_input("_down_comment") if not comment: raise MKUserError("_down_comment", _("You need to supply a comment for your downtime.")) if html.request.var("_down_flexible"): fixed = 0 duration = html.get_time_input("_down_duration", _("the duration")) else: fixed = 1 duration = 0 if html.get_checkbox("_down_do_recur"): fixed_and_recurring = recurring_type * 2 + fixed else: fixed_and_recurring = fixed def make_command(spec, cmdtag): return ("SCHEDULE_" + cmdtag + "_DOWNTIME;%s;" % spec) + ("%d;%d;%d;0;%d;%s;" % ( down_from, down_to, fixed_and_recurring, duration, config.user.id, )) + livestatus.lqencode(comment) if "aggr_tree" in row: # BI mode commands = [] for site, host, service in bi.find_all_leaves(row["aggr_tree"]): if service: spec = "%s;%s" % (host, service) cmdtag = "SVC" else: spec = host cmdtag = "HOST" commands.append((site, make_command(spec, cmdtag))) else: if html.request.var("_include_childs"): # only for hosts specs = [spec] + self._get_child_hosts( row["site"], [spec], recurse=bool(html.request.var("_include_childs_recurse"))) elif html.request.var("_on_hosts"): # set on hosts instead of services specs = [spec.split(";")[0]] title += " the hosts of" cmdtag = "HOST" else: specs = [spec] commands = [make_command(spec, cmdtag) for spec in specs] return commands, title
def validate_start_url(value, varprefix): if not is_allowed_url(value): raise MKUserError( varprefix, _("The given value is not allowed. You may only configure " "relative URLs like <tt>dashboard.py?name=my_dashboard</tt>."))
def _validate_ldap_connection_id(self, value, varprefix): if value in [c["id"] for c in config.user_connections]: raise MKUserError( varprefix, _("This ID is already used by another connection. Please choose another one."), )
def _set(self, request): # Py2: This encoding here should be kept Otherwise and unicode encoded text will be written # into the configuration file with unknown side effects ruleset_name = six.ensure_str(request["ruleset_name"]) # 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: raise MKGeneralException("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 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 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') % html.render_text(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 = html.render_text(_('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 = html.render_text(_('log file %s on all hosts') % file_name) html.header(_("Acknowledge %s") % 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.message('<b>%s</b><p>%s</p>' % (_('Acknowledged %s') % ack_msg, _('Acknowledged all messages in %s.') % ack_msg)) html.footer()
def _validate_user_attributes(all_users, user_id, user_attrs, is_new_user=True): # Check user_id if is_new_user: if user_id in all_users: raise MKUserError( "user_id", _("This username is already being used by another user.")) vs_user_id = UserID(allow_empty=False) vs_user_id.validate_value(user_id, "user_id") else: if user_id not in all_users: raise MKUserError( None, _("The user you are trying to edit does not exist.")) # Full name alias = user_attrs.get("alias") if not alias: raise MKUserError( "alias", _("Please specify a full name or descriptive alias for the user.")) # Locking locked = user_attrs.get("locked") if user_id == config.user.id and locked: raise MKUserError("locked", _("You cannot lock your own account!")) # Authentication: Password or Secret if "automation_secret" in user_attrs: secret = user_attrs["automation_secret"] if len(secret) < 10: raise MKUserError( 'secret', _("Please specify a secret of at least 10 characters length.")) else: password = user_attrs.get("password") if password: verify_password_policy(password) # Email email = user_attrs.get("email") vs_email = EmailAddressUnicode() vs_email.validate_value(email, "email") # Idle timeout idle_timeout = user_attrs.get("idle_timeout") vs_user_idle_timeout = get_vs_user_idle_timeout() vs_user_idle_timeout.validate_value(idle_timeout, "idle_timeout") # Notification settings are only active if we do *not* have rule based notifications! rulebased_notifications = load_configuration_settings().get( "enable_rulebased_notifications") if not rulebased_notifications: # Notifications notifications_enabled = user_attrs.get("notification_enabled") # Check if user can receive notifications if notifications_enabled: if not email: raise MKUserError( "email", _('You have enabled the notifications but missed to configure a ' 'Email address. You need to configure your mail address in order ' 'to be able to receive emails.')) contactgroups = user_attrs.get("contactgroups") if not contactgroups: raise MKUserError( "notifications_enabled", _('You have enabled the notifications but missed to make the ' 'user member of at least one contact group. You need to make ' 'the user member of a contact group which has hosts assigned ' 'in order to be able to receive emails.')) roles = user_attrs.get("roles") if not roles: raise MKUserError( "role_user", _("Your user has no roles. Please assign at least one role." )) notification_method = user_attrs.get("notification_method") get_vs_flexible_notifications().validate_value(notification_method, "notification_method") else: fallback_contact = user_attrs.get("fallback_contact") if fallback_contact and not email: raise MKUserError( "email", _("You have enabled the fallback notifications but missed to configure an " "email address. You need to configure your mail address in order " "to be able to receive fallback notifications.")) # Custom user attributes for name, attr in userdb.get_user_attributes(): value = user_attrs.get(name) attr.valuespec().validate_value(value, "ua_" + name)
def validate_only_services(value, varprefix): for s in value: if s and s[0] != '!': return raise MKUserError(varprefix + "_0", _("The list of services will never match"))
def _wato_page_handler(current_mode: str, mode_permissions: Optional[List[PermissionName]], mode_class: Type[WatoMode]) -> None: # Check general permission for this mode if mode_permissions is not None and not user.may("wato.seeall"): _ensure_mode_permissions(mode_permissions) mode = mode_class() # Do actions (might switch mode) if transactions.is_transaction(): try: user.need_permission("wato.edit") # Even if the user has seen this mode because auf "seeall", # he needs an explicit access permission for doing changes: if user.may("wato.seeall"): if mode_permissions: _ensure_mode_permissions(mode_permissions) if (cmk.gui.watolib.read_only.is_enabled() and not cmk.gui.watolib.read_only.may_override()): raise MKUserError(None, cmk.gui.watolib.read_only.message()) result = mode.action() if isinstance(result, (tuple, str, bool)): raise MKGeneralException( f'WatoMode "{current_mode}" returns unsupported return value: {result!r}' ) # We assume something has been modified and increase the config generation ID by one. update_config_generation() if config.wato_use_git: do_git_commit() # Handle two cases: # a) Don't render the page content after action # (a confirm dialog is displayed by the action, or a non-HTML content was sent) # b) Redirect to another page if isinstance(result, FinalizeRequest): raise result except MKUserError as e: user_errors.add(e) except MKAuthException as e: user_errors.add(MKUserError(None, e.args[0])) breadcrumb = make_main_menu_breadcrumb( mode.main_menu()) + mode.breadcrumb() page_menu = mode.page_menu(breadcrumb) wato_html_head( title=mode.title(), breadcrumb=breadcrumb, page_menu=page_menu, show_body_start=display_options.enabled(display_options.H), show_top_heading=display_options.enabled(display_options.T), ) if not transactions.is_transaction() or ( cmk.gui.watolib.read_only.is_enabled() and cmk.gui.watolib.read_only.may_override()): _show_read_only_warning() # Show outcome of failed action on this page html.show_user_errors() # Show outcome of previous page (that redirected to this one) for message in get_flashed_messages(): html.show_message(message) # Show content mode.handle_page() if is_sidebar_reload_needed(): html.reload_whole_page() wato_html_footer(show_body_end=display_options.enabled(display_options.H))
def _validate_alias(self, value, varprefix): unique, message = watolib.is_alias_used("timeperiods", self._name, value) if not unique: raise MKUserError("alias", message)
def _validate_id(self, value, varprefix): if value in self._timeperiods: raise MKUserError( varprefix, _("This name is already being used by another timeperiod."))
def _verify_empty(value, varprefix): if not value: raise MKUserError(varprefix, _("Please specify at least one type of levels"))
def edit_dictionaries( dictionaries: 'Sequence[Tuple[str, Union[Transform, Dictionary]]]', value: Dict[str, Any], focus: Optional[str] = None, hover_help: bool = True, validate: Optional[Callable[[Any], None]] = None, title: Optional[str] = None, method: str = "GET", preview: bool = False, varprefix: str = "", formname: str = "form", consume_transid: bool = True): if html.request.get_ascii_input( "filled_in") == formname and 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 html.request.del_var("filled_in") html.hidden_fields() html.end_form()
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 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 _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]) # TODO: This *way* too general, even for our very low standards... counts = {"new": 0, "old": 0} # type: Dict[Any, Any] 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 = dict([(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) operation = OperationReplaceGroupedTags(self._tag_group.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 != True and message or None return "tags"
def _comment(self): comment = html.request.get_unicode_input("_down_comment") if not comment: raise MKUserError("_down_comment", _("You need to supply a comment for your downtime.")) return comment
def get_request(self) -> SiteRequest: ascii_input = request.get_ascii_input("request") if ascii_input is None: raise MKUserError("request", _('The parameter "%s" is missing.') % "request") return SiteRequest.deserialize(ast.literal_eval(ascii_input))
def _rename_tags_after_confirmation( breadcrumb: Breadcrumb, operation: 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. Returns: True: Proceed, no "question" dialog shown False: "Question dialog" shown str: Action done after "question" dialog """ 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 = HTML() 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(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(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(html.drain()) if message: wato_html_head(title=operation.confirm_title(), breadcrumb=breadcrumb) 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 _do_login(self) -> None: """handle the sent login form""" if not request.var("_login"): return try: if not config.user_login: raise MKUserError(None, _("Login is not allowed on this site.")) username_var = request.get_str_input("_username", "") assert username_var is not None username = UserId(username_var.rstrip()) if not username: raise MKUserError("_username", _("Missing username")) password = request.var("_password", "") if not password: raise MKUserError("_password", _("Missing password")) default_origtarget = url_prefix() + "check_mk/" origtarget = request.get_url_input("_origtarget", default_origtarget) # Disallow redirections to: # - logout.py: Happens after login # - side.py: Happens when invalid login is detected during sidebar refresh if "logout.py" in origtarget or "side.py" in origtarget: origtarget = default_origtarget result = userdb.check_credentials(username, password) if result: # use the username provided by the successful login function, this function # might have transformed the username provided by the user. e.g. switched # from mixed case to lower case. username = result session_id = userdb.on_succeeded_login(username) # The login succeeded! Now: # a) Set the auth cookie # b) Unset the login vars in further processing # c) Redirect to really requested page _create_auth_session(username, session_id) # Never use inplace redirect handling anymore as used in the past. This results # in some unexpected situations. We simpy use 302 redirects now. So we have a # clear situation. # userdb.need_to_change_pw returns either False or the reason description why the # password needs to be changed change_pw_result = userdb.need_to_change_pw(username) if change_pw_result: raise HTTPRedirect( "user_change_pw.py?_origtarget=%s&reason=%s" % (urlencode(origtarget), change_pw_result)) if userdb.is_two_factor_login_enabled(username): raise HTTPRedirect( "user_login_two_factor.py?_origtarget=%s" % urlencode(makeuri(request, []))) raise HTTPRedirect(origtarget) userdb.on_failed_login(username) raise MKUserError(None, _("Invalid login")) except MKUserError as e: user_errors.add(e)
def page_edit(cls): back_url = html.get_url_input("back", cls.list_url()) cls.load() cls.need_overriding_permission("edit") # Three possible modes: # "create" -> create completely new page # "clone" -> like new, but prefill form with values from existing page # "edit" -> edit existing page mode = html.request.var('mode', 'edit') if mode == "create": title = cls.phrase("create") page_dict = { "name": cls.default_name(), "topic": cls.default_topic(), } else: # Load existing page. visual from disk - and create a copy if 'load_user' is set page_name = html.request.var("load_name") if mode == "edit": title = cls.phrase("edit") owner_user_id = UserId( html.request.get_unicode_input_mandatory("owner", config.user.id)) if owner_user_id == config.user.id: page = cls.find_my_page(page_name) else: page = cls.find_foreign_page(owner_user_id, page_name) if page is None: raise MKUserError(None, _("The requested %s does not exist") % cls.phrase("title")) # TODO FIXME: Looks like a hack cls.remove_instance((owner_user_id, page_name)) # will be added later again else: # clone title = cls.phrase("clone") load_user = html.request.get_unicode_input( "load_user") # FIXME: Change varname to "owner" try: page = cls.instance((load_user, page_name)) except KeyError: raise MKUserError(None, _("The requested %s does not exist") % cls.phrase("title")) page_dict = page.internal_representation() html.header(title) html.begin_context_buttons() html.context_button(_("Back"), back_url, "back") html.end_context_buttons() parameters, keys_by_topic = cls._collect_parameters(mode) vs = Dictionary( title=_("General Properties"), render='form', optional_keys=False, elements=parameters, headers=keys_by_topic, ) def validate(page_dict): owner_user_id = UserId(html.request.get_unicode_input_mandatory( "owner", config.user.id)) page_name = page_dict["name"] if owner_user_id == config.user.id: page = cls.find_my_page(page_name) else: page = cls.find_foreign_page(owner_user_id, page_name) if page: raise MKUserError( "_p_name", _("You already have an element with the ID <b>%s</b>") % page_dict["name"]) new_page_dict = forms.edit_valuespec(vs, page_dict, validate=validate, focus="_p_title", method="POST") if new_page_dict is not None: # Take over keys from previous value that are specific to the page type # and not edited here. if mode in ("edit", "clone"): for key, value in page_dict.items(): new_page_dict.setdefault(key, value) owner = UserId(html.request.get_unicode_input_mandatory("owner", config.user.id)) new_page_dict["owner"] = owner new_page = cls(new_page_dict) cls.add_page(new_page) cls.save_user_instances(owner) if mode == "create": redirect_url = new_page.after_create_url() or back_url else: redirect_url = back_url html.immediate_browser_redirect(0.5, redirect_url) html.show_message(_('Your changes haven been saved.')) # Reload sidebar.TODO: This code logically belongs to PageRenderer. How # can we simply move it there? # TODO: This is not true for all cases. e.g. the BookmarkList is not # of type PageRenderer but has a dedicated sidebar snapin. Maybe # the best option would be to make a dedicated method to decide whether # or not to reload the sidebar. if new_page_dict.get("hidden") in [ None, False ] \ or new_page_dict.get("hidden") != page_dict.get("hidden"): html.reload_sidebar() else: html.show_localization_hint() html.footer()
def test_user_error(request_context): with output_funnel.plugged(): html.user_error( MKUserError(None, "asd <script>alert(1)</script> <br> <b>")) c = output_funnel.drain() assert c == '<div class="error">asd <script>alert(1)</script> <br> <b></div>'
def _require_metric(value, varprefix): if value is None: raise MKUserError(varprefix, _("You need to select a metric"))
def _wato_page_handler(current_mode: str, mode_permissions: List[PermissionName], mode_class: Type[WatoMode]) -> None: try: init_wato_datastructures(with_wato_lock=not html.is_transaction()) except Exception: # Snapshot must work in any case if current_mode == 'snapshot': pass else: raise # Check general permission for this mode if mode_permissions is not None and not config.user.may("wato.seeall"): _ensure_mode_permissions(mode_permissions) mode = mode_class() # Do actions (might switch mode) action_message: Optional[str] = None if html.is_transaction(): try: config.user.need_permission("wato.edit") # Even if the user has seen this mode because auf "seeall", # he needs an explicit access permission for doing changes: if config.user.may("wato.seeall"): if mode_permissions: _ensure_mode_permissions(mode_permissions) if cmk.gui.watolib.read_only.is_enabled( ) and not cmk.gui.watolib.read_only.may_override(): raise MKUserError(None, cmk.gui.watolib.read_only.message()) result = mode.action() if isinstance(result, tuple): newmode, action_message = result else: newmode = result # We assume something has been modified and increase the config generation ID by one. update_config_generation() # If newmode is False, then we shall immediately abort. # This is e.g. the case, if the page outputted non-HTML # data, such as a tarball (in the export function). We must # be sure not to output *any* further data in that case. if newmode is False: return # if newmode is not None, then the mode has been changed if newmode is not None: assert not isinstance(newmode, bool) if newmode == "": # no further information: configuration dialog, etc. if action_message: html.show_message(action_message) wato_html_footer() return mode_permissions, mode_class = _get_mode_permission_and_class( newmode) current_mode = newmode mode = mode_class() html.request.set_var("mode", newmode) # will be used by makeuri # Check general permissions for the new mode if mode_permissions is not None and not config.user.may( "wato.seeall"): for pname in mode_permissions: if '.' not in pname: pname = "wato." + pname config.user.need_permission(pname) except MKUserError as e: action_message = "%s" % e html.add_user_error(e.varname, action_message) except MKAuthException as e: reason = e.args[0] action_message = reason html.add_user_error(None, reason) breadcrumb = make_main_menu_breadcrumb( mode.main_menu()) + mode.breadcrumb() wato_html_head(mode.title(), breadcrumb=breadcrumb, show_body_start=display_options.enabled(display_options.H), show_top_heading=display_options.enabled(display_options.T)) if display_options.enabled(display_options.B): # Show contexts buttons html.begin_context_buttons() mode.buttons() html.end_context_buttons() if not html.is_transaction() or (cmk.gui.watolib.read_only.is_enabled() and cmk.gui.watolib.read_only.may_override()): _show_read_only_warning() # Show outcome of action if html.has_user_errors(): html.show_user_errors() elif action_message: html.show_message(action_message) # Show content mode.handle_page() if is_sidebar_reload_needed(): html.reload_sidebar() if config.wato_use_git and html.is_transaction(): do_git_commit() wato_html_footer(show_footer=display_options.enabled(display_options.Z), show_body_end=display_options.enabled(display_options.H))
def action(self): if not html.check_transaction(): return "users" if self._user_id is None: # same as self._is_new_user self._user_id = UserID(allow_empty=False).from_html_vars("user_id") user_attrs = {} else: self._user_id = html.request.get_unicode_input_mandatory( "edit").strip() user_attrs = self._users[UserId(self._user_id)] # Full name user_attrs["alias"] = html.request.get_unicode_input_mandatory( "alias").strip() # Locking user_attrs["locked"] = html.get_checkbox("locked") increase_serial = False if (UserId(self._user_id) in self._users and self._users[UserId( 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.get_str_input_mandatory("_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.get_str_input_mandatory( "_password_" + self._pw_suffix(), '').strip() password2 = html.request.get_str_input_mandatory( "_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"] = EmailAddress().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.get_str_input_mandatory( "pager", '').strip() if cmk_version.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 language = html.request.get_ascii_input_mandatory("language", "") if language != "_default_": user_attrs["language"] = language elif "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 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"))