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() _changes.add_change( "edit-roles", _("Deleted role '%s'") % delid, sites=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 groups.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() _changes.add_change( "edit-roles", _("Created new role '%s'") % newid, sites=get_login_sites() ) return redirect(self.mode_url())
def page(self) -> None: if not user.may("wato.diagnostics"): raise MKAuthException( _("Sorry, you lack the permission for downloading diagnostics dumps." )) site = SiteId(request.get_ascii_input_mandatory("site")) tarfile_name = request.get_ascii_input_mandatory("tarfile_name") file_content = self._get_diagnostics_dump_file(site, tarfile_name) response.set_content_type("application/x-tgz") response.headers[ "Content-Disposition"] = "Attachment; filename=%s" % tarfile_name response.set_data(file_content)
def _get_search_vars(self) -> HTTPVariables: search_vars = {} if request.has_var("host_search_host"): search_vars[ "host_search_host"] = request.get_ascii_input_mandatory( "host_search_host") for varname, value in request.itervars(prefix="host_search_change_"): if html.get_checkbox(varname) is False: continue search_vars[varname] = value attr_ident = varname.split("host_search_change_", 1)[1] # The URL variable naming scheme is not clear. Try to match with "attr_" prefix # and without. We should investigate and clean this up. attr_prefix = "host_search_attr_%s" % attr_ident search_vars.update(request.itervars(prefix=attr_prefix)) attr_prefix = "host_search_%s" % attr_ident search_vars.update(request.itervars(prefix=attr_prefix)) for varname, value in request.itervars(): if varname.startswith(("_", "host_search_")) or varname == "mode": continue search_vars[varname] = value search_vars["mode"] = "folder" return list(search_vars.items())
def _from_vars(self): self._role_id = request.get_ascii_input_mandatory("edit") try: self._role = self._roles[self._role_id] except KeyError: raise MKUserError("edit", _("This role does not exist."))
def _action(self) -> None: assert user.id is not None users = userdb.load_users(lock=True) user_spec = users[user.id] language = request.get_ascii_input_mandatory("language", "") # Set the users language if requested to set it explicitly if language != "_default_": user_spec["language"] = language user.language = language set_language_cookie(request, response, language) else: if "language" in user_spec: del user_spec["language"] user.reset_language() # load the new language localize(user.language) if user.may("general.edit_notifications") and user_spec.get( "notifications_enabled"): value = _get_input(get_vs_flexible_notifications(), "notification_method") user_spec["notification_method"] = value # Custom attributes if user.may("general.edit_user_attributes"): for name, attr in userdb.get_user_attributes(): if not attr.user_editable(): continue perm_name = attr.permission() if perm_name and not user.may(perm_name): continue vs = attr.valuespec() value = vs.from_html_vars("ua_" + name) vs.validate_value(value, "ua_" + name) # TODO: Dynamically fiddling around with a TypedDict is a bit questionable user_spec[name] = value # type: ignore[literal-required] userdb.save_users(users, datetime.now()) flash(_("Successfully updated user profile.")) # In distributed setups with remote sites where the user can login, start the # user profile replication now which will redirect the user to the destination # page after completion. Otherwise directly open up the destination page. if user.authorized_login_sites(): back_url = "user_profile_replicate.py?back=user_profile.py" else: back_url = "user_profile.py" # Ensure theme changes are applied without additional user interaction html.reload_whole_page(back_url) html.footer() raise FinalizeRequest(code=200)
def inpage_search_form(mode: Optional[str] = None, default_value: str = "") -> None: form_name = "inpage_search_form" reset_button_id = "%s_reset" % form_name was_submitted = request.get_ascii_input("filled_in") == form_name html.begin_form(form_name, add_transid=False) html.text_input( "search", size=32, default_value=default_value, placeholder=_("Filter"), required=True, title="", ) html.hidden_fields() if mode: html.hidden_field("mode", mode, add_var=True) reset_url = request.get_ascii_input_mandatory("reset_url", requested_file_with_query(request)) html.hidden_field("reset_url", reset_url, add_var=True) html.button("submit", "", cssclass="submit", help_=_("Apply")) html.buttonlink(reset_url, "", obj_id=reset_button_id, title=_("Reset")) html.end_form() html.javascript( "cmk.page_menu.inpage_search_init(%s, %s)" % (json.dumps(reset_button_id), json.dumps(was_submitted)) )
def get_request(self) -> Sequence[HostName]: raw_host_names = json.loads( request.get_ascii_input_mandatory("host_names", "[]")) return [ HostName(raw_host_name) for raw_host_name in raw_host_names if self._validate_host_name(raw_host_name) ]
def _show_form(self) -> None: assert user.id is not None credentials = load_two_factor_credentials(user.id) credential_id = request.get_ascii_input_mandatory("_edit") credential = credentials["webauthn_credentials"].get(credential_id) if credential is None: raise MKUserError("_edit", _("The credential does not exist")) html.begin_form("profile", method="POST") html.prevent_password_auto_completion() html.open_div(class_="wato") self._valuespec(credential).render_input( "profile", { "registered_at": credential["registered_at"], "alias": credential["alias"], }, ) forms.end() html.close_div() html.hidden_field("_edit", credential_id) html.hidden_fields() html.end_form() html.footer()
def _action(self) -> None: assert user.id is not None credentials = load_two_factor_credentials(user.id, lock=True) credential_id = request.get_ascii_input_mandatory("_edit") credential = credentials["webauthn_credentials"].get(credential_id) if credential is None: raise MKUserError("_edit", _("The credential does not exist")) vs = self._valuespec(credential) settings = vs.from_html_vars("profile") vs.validate_value(settings, "profile") credential["alias"] = settings["alias"] save_two_factor_credentials(user.id, credentials) flash(_("Successfully changed the credential.")) # 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 = "user_two_factor_overview.py" if user.authorized_login_sites(): raise redirect( makeuri_contextless(request, [("back", origtarget)], filename="user_profile_replicate.py")) raise redirect(origtarget)
def _from_vars(self): self._hostname = request.get_ascii_input_mandatory("host") self._host = Folder.current().load_host(self._hostname) self._host.need_permission("read") if self._host.is_cluster(): raise MKGeneralException(_("This page does not support cluster hosts."))
def page_api() -> None: try: if not request.has_var("output_format"): response.set_content_type("application/json") output_format = "json" else: output_format = request.get_ascii_input_mandatory( "output_format", "json").lower() if output_format not in _FORMATTERS: response.set_content_type("text/plain") raise MKUserError( None, "Only %s are supported as output formats" % " and ".join('"%s"' % f for f in _FORMATTERS), ) # TODO: Add some kind of helper for boolean-valued variables? pretty_print = False pretty_print_var = request.get_str_input_mandatory( "pretty_print", "no").lower() if pretty_print_var not in ("yes", "no"): raise MKUserError(None, 'pretty_print must be "yes" or "no"') pretty_print = pretty_print_var == "yes" api_call = _get_api_call() _check_permissions(api_call) request_object = _get_request(api_call) _check_formats(output_format, api_call, request_object) _check_request_keys(api_call, request_object) resp = _execute_action(api_call, request_object) except MKAuthException as e: resp = { "result_code": 1, "result": _("Authorization Error. Insufficent permissions for '%s'") % e, } except MKException as e: resp = { "result_code": 1, "result": _("Checkmk exception: %s\n%s") % (e, "".join(traceback.format_exc())), } except Exception: if active_config.debug: raise logger.exception("error handling web API call") resp = { "result_code": 1, "result": _("Unhandled exception: %s") % traceback.format_exc(), } response.set_data( _FORMATTERS[output_format][1 if pretty_print else 0](resp))
def page(self): if not user.may("wato.automation"): raise MKAuthException( _("This account has no permission for automation.")) response.set_content_type("text/plain") _set_version_headers() # Parameter was added with 1.5.0p10 if not request.has_var("_version"): raise MKGeneralException( _("Your central site is incompatible with this remote site")) # - _version and _edition_short were added with 1.5.0p10 to the login call only # - x-checkmk-version and x-checkmk-edition were added with 2.0.0p1 # Prefer the headers and fall back to the request variables for now. central_version = (request.headers["x-checkmk-version"] if "x-checkmk-version" in request.headers else request.get_ascii_input_mandatory("_version")) central_edition_short = ( request.headers["x-checkmk-edition"] if "x-checkmk-edition" in request.headers else request.get_ascii_input_mandatory("_edition_short")) if not compatible_with_central_site( central_version, central_edition_short, cmk_version.__version__, cmk_version.edition().short, ): raise MKGeneralException( _("Your central site (Version: %s, Edition: %s) is incompatible with this " "remote site (Version: %s, Edition: %s)") % ( central_version, central_edition_short, cmk_version.__version__, cmk_version.edition().short, )) response.set_data( repr({ "version": cmk_version.__version__, "edition_short": cmk_version.edition().short, "login_secret": _get_login_secret(create_on_demand=True), }))
def _init_host(self) -> CREHost: hostname = request.get_ascii_input_mandatory("host") folder = Folder.current() if not folder.has_host(hostname): raise MKUserError( "host", _("You called this page with an invalid host name.")) host = folder.load_host(hostname) host.need_permission("read") return host
def _from_vars(self): self._hostname = request.get_ascii_input_mandatory("host") host = Folder.current().host(self._hostname) if host is None: raise MKUserError("host", _("The given host does not exist.")) self._host: CREHost = host self._host.need_permission("read") # TODO: Validate? self._service = request.get_str_input("service")
def _from_vars(self): self._start = bool(request.var("_start")) # 'all' not set -> only scan checked hosts in current folder, no recursion # otherwise: all host in this folder, maybe recursively self._all = bool(request.var("all")) self._complete_folder = self._all # Ignored during initial form display self._settings = ParentScanSettings( where=request.get_ascii_input_mandatory("where", "subfolder"), alias=request.get_str_input_mandatory("alias", "").strip(), recurse=html.get_checkbox("recurse") or False, select=request.get_ascii_input_mandatory("select", "noexplicit"), timeout=request.get_integer_input_mandatory("timeout", 8), probes=request.get_integer_input_mandatory("probes", 2), max_ttl=request.get_integer_input_mandatory("max_ttl", 10), force_explicit=html.get_checkbox("force_explicit") or False, ping_probes=request.get_integer_input_mandatory("ping_probes", 5), ) self._job = ParentScanBackgroundJob()
def get_request(self): site_id = SiteId(request.get_ascii_input_mandatory("site_id")) activate_changes.verify_remote_site_config(site_id) try: serialized_domain_requests = ast.literal_eval( request.get_ascii_input_mandatory("domains")) if serialized_domain_requests and isinstance( serialized_domain_requests[0], str): serialized_domain_requests = [ asdict(DomainRequest(x)) for x in serialized_domain_requests ] except SyntaxError: raise MKAutomationException( _("Invalid request: %r") % request.get_ascii_input_mandatory("domains")) return ActivateChangesRequest(site_id=site_id, domains=serialized_domain_requests)
def _ajax_switch_masterstate(self) -> None: response.set_content_type("text/plain") if not user.may("sidesnap.master_control"): return if not transactions.check_transaction(): return site = SiteId(request.get_ascii_input_mandatory("site")) column = request.get_ascii_input_mandatory("switch") state = request.get_integer_input_mandatory("state") commands = { ("enable_notifications", 1): "ENABLE_NOTIFICATIONS", ("enable_notifications", 0): "DISABLE_NOTIFICATIONS", ("execute_service_checks", 1): "START_EXECUTING_SVC_CHECKS", ("execute_service_checks", 0): "STOP_EXECUTING_SVC_CHECKS", ("execute_host_checks", 1): "START_EXECUTING_HOST_CHECKS", ("execute_host_checks", 0): "STOP_EXECUTING_HOST_CHECKS", ("enable_flap_detection", 1): "ENABLE_FLAP_DETECTION", ("enable_flap_detection", 0): "DISABLE_FLAP_DETECTION", ("process_performance_data", 1): "ENABLE_PERFORMANCE_DATA", ("process_performance_data", 0): "DISABLE_PERFORMANCE_DATA", ("enable_event_handlers", 1): "ENABLE_EVENT_HANDLERS", ("enable_event_handlers", 0): "DISABLE_EVENT_HANDLERS", } command = commands.get((column, state)) if not command: html.write_text(_("Command %s/%d not found") % (column, state)) return sites.live().command("[%d] %s" % (int(time.time()), command), site) sites.live().set_only_sites([site]) sites.live().query( "GET status\nWaitTrigger: program\nWaitTimeout: 10000\nWaitCondition: %s = %d\nColumns: %s\n" % (column, state, column) ) sites.live().set_only_sites() self.show()
def _from_vars(self): host_name = request.get_ascii_input_mandatory("host") if not Folder.current().has_host(host_name): raise MKUserError( "host", _("You called this page with an invalid host name.")) if not user.may("wato.rename_hosts"): raise MKAuthException( _("You don't have the right to rename hosts")) self._host = Folder.current().load_host(host_name) self._host.need_permission("write")
def _from_vars(self): self._varname = request.get_ascii_input_mandatory("varname") try: self._config_variable = config_variable_registry[self._varname]() self._valuespec = self._config_variable.valuespec() except KeyError: raise MKUserError( "varname", _('The global setting "%s" does not exist.') % self._varname) if not self._may_edit_configvar(self._varname): raise MKAuthException( _("You are not permitted to edit this global setting.")) self._current_settings = load_configuration_settings() self._global_settings = {}
def action(self) -> ActionResult: if not transactions.check_transaction(): return redirect(mode_url("%s_groups" % self.type_name)) alias = request.get_str_input_mandatory("alias").strip() self.group = {"alias": alias} self._determine_additional_group_data() if self._new: self._name = request.get_ascii_input_mandatory("name").strip() groups.add_group(self._name, self.type_name, self.group) else: assert self._name is not None groups.edit_group(self._name, self.type_name, self.group) return redirect(mode_url("%s_groups" % self.type_name))
def action(self) -> ActionResult: if not transactions.transaction_valid(): return redirect(mode_url("folder")) attributes = collect_attributes(self._host_type_name(), new=True) cluster_nodes = self._get_cluster_nodes() hostname = request.get_ascii_input_mandatory("host") Hostname().validate_value(hostname, "host") folder = Folder.current() if transactions.check_transaction(): folder.create_hosts([(hostname, attributes, cluster_nodes)]) self._host = folder.load_host(hostname) inventory_url = folder_preserving_link([ ("mode", "inventory"), ("host", self._host.name()), ("_scan", "1"), ]) create_msg = (None if self._host.is_ping_host() else ( _("Successfully created the host. Now you should do a " '<a href="%s">service discovery</a> in order to auto-configure ' "all services to be checked on this host.") % inventory_url)) if request.var("_save"): return redirect(inventory_url) if create_msg: flash(create_msg) if request.var("diag_host"): return redirect( mode_url("diag_host", folder=folder.path(), host=self._host.name(), _try="1")) return redirect(mode_url("folder", folder=folder.path()))
def _from_vars(self) -> None: self._check_plugin_name = request.get_ascii_input_mandatory( "check_type", "") check_builtins = ["check-mk", "check-mk-inventory"] if (not re.match("^[a-zA-Z0-9_.]+$", self._check_plugin_name) and self._check_plugin_name not in check_builtins): raise MKUserError("check_type", _("Invalid check type")) manpage = man_pages.load_man_page(self._check_plugin_name) if manpage is None: raise MKUserError(None, _("There is no manpage for this check.")) self._manpage = manpage checks = get_check_information().plugin_infos if (check_info := checks.get(self._check_plugin_name)) is not None: self._check_type = "check_mk" self._service_description = check_info["service_description"] self._ruleset: Optional[ str] = f"checkgroup_parameters:{check_info['check_ruleset_name']}"
def action(self) -> ActionResult: if not transactions.check_transaction(): return redirect(mode_url("%s_groups" % self.type_name)) if request.var("_delete"): delname = request.get_ascii_input_mandatory("_delete") usages = groups.find_usages_of_group(delname, self.type_name) if usages: message = "<b>%s</b><br>%s:<ul>" % ( _("You cannot delete this %s group.") % self.type_name, _("It is still in use by"), ) for title, link in usages: message += '<li><a href="%s">%s</a></li>\n' % (link, title) message += "</ul>" raise MKUserError(None, message) groups.delete_group(delname, self.type_name) self._groups = self._load_groups() return redirect(mode_url("%s_groups" % self.type_name))
def _from_vars(self): self._topic = request.get_ascii_input_mandatory("topic", "") if not re.match("^[a-zA-Z0-9_./]+$", self._topic): raise MKUserError("topic", _("Invalid topic")) self._path: Tuple[str, ...] = tuple( self._topic.split("/")) # e.g. [ "hw", "network" ] for comp in self._path: ID().validate_value(comp, None) # Beware against code injection! self._manpages = _get_check_catalog(self._path) self._titles = man_pages.CATALOG_TITLES self._has_second_level = None for t, has_second_level, title, _helptext in _man_page_catalog_topics( ): if t == self._path[0]: self._has_second_level = has_second_level self._topic_title = title break if len(self._path) == 2: self._topic_title = self._titles.get(self._path[1], self._path[1])
def _answer_graph_image_request() -> None: try: host_name = request.get_ascii_input_mandatory("host") if not host_name: raise MKGeneralException(_('Missing mandatory "host" parameter')) service_description = request.get_str_input_mandatory("service", "_HOST_") site = request.var("site") # FIXME: We should really enforce site here. But it seems that the notification context # has no idea about the site of the host. This could be optimized later. # if not site: # raise MKGeneralException("Missing mandatory \"site\" parameter") try: row = get_graph_data_from_livestatus(site, host_name, service_description) except livestatus.MKLivestatusNotFoundError: if active_config.debug: raise raise Exception( _("Cannot render graph: host %s, service %s not found.") % (host_name, service_description) ) site = row["site"] # Always use 25h graph in notifications end_time = int(time.time()) start_time = end_time - (25 * 3600) graph_render_options = graph_image_render_options() graph_identification: tuple[Literal["template"], TemplateGraphSpec] = ( "template", TemplateGraphSpec( { "site": site, "host_name": host_name, "service_description": service_description, "graph_index": None, # all graphs } ), ) graph_data_range = graph_image_data_range(graph_render_options, start_time, end_time) graph_recipes = graph_identification_types.create_graph_recipes( graph_identification, destination=html_render.GraphDestinations.notification ) num_graphs = request.get_integer_input("num_graphs") or len(graph_recipes) graphs = [] for graph_recipe in graph_recipes[:num_graphs]: graph_artwork = artwork.compute_graph_artwork( graph_recipe, graph_data_range, graph_render_options ) graph_png = render_graph_image(graph_artwork, graph_data_range, graph_render_options) graphs.append(base64.b64encode(graph_png).decode("ascii")) response.set_data(json.dumps(graphs)) except Exception as e: logger.error("Call to ajax_graph_images.py failed: %s\n%s", e, traceback.format_exc()) if active_config.debug: raise
def action(self) -> ActionResult: if html.form_submitted("search"): return None alias = request.get_str_input_mandatory("alias") unique, info = groups.is_alias_used("roles", self._role_id, alias) if not unique: assert info is not None 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 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() _changes.add_change( "edit-roles", _("Modified user role '%s'") % new_id, sites=get_login_sites() ) return redirect(mode_url("roles"))
def get_request(self) -> CheckmkAutomationRequest: return CheckmkAutomationRequest( *ast.literal_eval(request.get_ascii_input_mandatory("request")))
def get_request(self) -> str: return ast.literal_eval(request.get_ascii_input_mandatory("request"))
def get_request(self) -> str: return request.get_ascii_input_mandatory("tarfile_name")
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_str_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_str_input_mandatory("topic", "").strip() help_txt = request.get_str_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) _changes.add_change( "edit-%sattr" % self._type, _("Create new %s attribute %s") % (self._type, self._name), ) else: _changes.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"))