def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): painted_rows = [] header_row = [] for cell in cells: header_row.append(escaping.strip_tags(cell.export_title())) painted_rows.append(header_row) for row in rows: painted_row = [] for cell in cells: joined_row = join_row(row, cell) content = cell.render_for_export(joined_row) if isinstance(content, (list, dict)): # Allow painters to return lists and dicts, then json encode them # as such data structures without wrapping them into strings pass else: content = "%s" % content content = escaping.strip_tags(content.replace( "<br>", "\n")) painted_row.append(content) painted_rows.append(painted_row) html.write(json.dumps(painted_rows, indent=True))
def _show_json(view: "View", rows: Rows) -> None: painted_rows = [] header_row = [] for cell in view.row_cells: header_row.append(escaping.strip_tags(cell.export_title())) painted_rows.append(header_row) for row in rows: painted_row = [] for cell in view.row_cells: joined_row = join_row(row, cell) content = cell.render_for_export(joined_row) if isinstance(content, (list, dict)): # Allow painters to return lists and dicts, then json encode them # as such data structures without wrapping them into strings pass else: content = escaping.strip_tags( str(content).replace("<br>", "\n")) painted_row.append(content) painted_rows.append(painted_row) html.write(json.dumps(painted_rows, indent=True))
def _write_csv(self, csv_separator): rows = self.rows headers = self.headers limit = self.limit omit_headers = self.options["omit_headers"] # Apply limit after search / sorting etc. if limit is not None: rows = rows[:limit] # If we have no group headers then paint the headers now if not omit_headers and self.rows and self.rows[0][2] != "header": html.write( csv_separator.join([ escaping.strip_tags(header) or "" for (header, _css, _help, _sortable) in headers ]) + "\n") for row_spec, _css, _state, _fixed, _attrs in rows: html.write( csv_separator.join([ escaping.strip_tags(cell_content) for cell_content, _css_classes, _colspan in row_spec ])) html.write("\n")
def _write_csv(self, csv_separator: str) -> None: rows = self.rows limit = self.limit omit_headers = self.options["omit_headers"] # Apply limit after search / sorting etc. if limit is not None: rows = rows[:limit] # If we have no group headers then paint the headers now if not omit_headers and self.rows and not isinstance( self.rows[0], GroupHeader): html.write( csv_separator.join([ escaping.strip_tags(header.title) or "" for header in self.headers ]) + "\n") for row in rows: if isinstance(row, GroupHeader): continue html.write( csv_separator.join( [escaping.strip_tags(cell.content) for cell in row.cells])) html.write("\n")
def section(title=None, checkbox=None, section_id=None, simple=False, hide=False, legend=True, css=None): # type: (Union[None, HTML, str], Union[None, HTML, str, Tuple[str, bool, str]], Optional[str], bool, bool, bool, Optional[str]) -> None global g_section_open if g_section_open: html.close_td() html.close_tr() html.open_tr(id_=section_id, class_=[css], style="display:none;" if hide else None) if legend: html.open_td(class_=["legend", "simple" if simple else None]) if title: html.open_div(class_=["title", "withcheckbox" if checkbox else None], title=escaping.strip_tags(title)) html.write(escaping.escape_text(title)) html.span('.' * 200, class_="dots") html.close_div() if checkbox: html.open_div(class_="checkbox") if isinstance(checkbox, (str, HTML)): html.write(checkbox) else: name, active, attrname = checkbox html.checkbox(name, active, onclick='cmk.wato.toggle_attribute(this, \'%s\')' % attrname) html.close_div() html.close_td() html.open_td(class_=["content", "simple" if simple else None]) g_section_open = True
def _sort_rows(rows: TableRows, sort_col: int, sort_reverse: int) -> TableRows: # remove and remind fixed rows, add to separate list fixed_rows = [] for index, row in enumerate(rows[:]): if row.fixed: rows.remove(row) fixed_rows.append((index, row)) # Then use natural sorting to sort the list. Note: due to a # change in the number of columns of a table in different software # versions the cmp-function might fail. This is because the sorting # column is persisted in a user file. So we ignore exceptions during # sorting. This gives the user the chance to change the sorting and # see the table in the first place. try: rows.sort(key=lambda x: utils.key_num_split( escaping.strip_tags(x[0][sort_col][0])), reverse=sort_reverse == 1) except IndexError: pass # Now re-add the removed "fixed" rows to the list again if fixed_rows: for index, cells in fixed_rows: rows.insert(index, cells) return rows
def render(self, row, cell): classes = ["perfometer"] if is_stale(row): classes.append("stale") try: title, h = Perfometer(row).render() if title is None and h is None: return "", "" except Exception as e: logger.exception("error rendering performeter") if config.debug: raise return " ".join(classes), _("Exception: %s") % e content = html.render_div(HTML(h), class_=["content"]) \ + html.render_div(title, class_=["title"]) \ + html.render_div("", class_=["glass"]) # pnpgraph_present: -1 means unknown (path not configured), 0: no, 1: yes if display_options.enabled(display_options.X) \ and row["service_pnpgraph_present"] != 0: url = cmk_graph_url(row, "service") disabled = False else: url = "javascript:void(0)" disabled = True return " ".join(classes), \ html.render_a(content=content, href=url, title=escaping.strip_tags(title), class_=["disabled" if disabled else None])
def _export_audit_log(self, audit: List[AuditLogStore.Entry]) -> ActionResult: html.set_output_format("csv") if self._options["display"] == "daily": filename = "wato-auditlog-%s_%s.csv" % (render.date( time.time()), render.time_of_day(time.time())) else: filename = "wato-auditlog-%s_%s_days.csv" % (render.date( time.time()), self._options["display"][1]) html.response.headers[ "Content-Disposition"] = "attachment; filename=\"%s\"" % filename titles = [ _('Date'), _('Time'), _('Object type'), _('Object'), _('User'), _('Action'), _('Summary'), ] if self._show_details: titles.append(_('Details')) html.write(','.join(titles) + '\n') for entry in audit: columns = [ render.date(int(entry.time)), render.time_of_day(int(entry.time)), entry.object_ref.object_type.name if entry.object_ref else "", entry.object_ref.ident if entry.object_ref else "", entry.user_id, entry.action, '"' + escaping.strip_tags(entry.text).replace('"', "'") + '"', ] if self._show_details: columns.append( '"' + escaping.strip_tags(entry.diff_text).replace('"', "'") + '"') html.write(','.join(columns) + '\n') return FinalizeRequest(code=200)
def _format_for_csv(self, raw_data): # raw_data can also be int, float, dict (labels) if isinstance(raw_data, dict): return ', '.join( ["%s: %s" % (key, value) for key, value in raw_data.items()]) return escaping.strip_tags(str(raw_data)).replace('\n', '').replace( '"', '""')
def log_audit(linkinfo, action, message, user_id=None): if config.wato_use_git: if isinstance(message, HTML): message = escaping.strip_tags(message.value) cmk.gui.watolib.git.add_message(message) # Using escape_attribute here is against our regular rule to do the escaping # at the last possible time: When rendering. But this here is the last # place where we can distinguish between HTML() encapsulated (already) # escaped / allowed HTML and strings to be escaped. message = escaping.escape_text(message).strip() log_entry(linkinfo, action, message, user_id)
def do_site_login(site_id, name, password): sites = SiteManagementFactory().factory().load_sites() site = sites[site_id] if not name: raise MKUserError( "_name", _("Please specify your administrator login on the remote site.")) if not password: raise MKUserError("_passwd", _("Please specify your password.")) # Trying basic auth AND form based auth to ensure the site login works. # Adding _ajaxid makes the web service fail silently with an HTTP code and # not output HTML code for an error screen. url = site["multisiteurl"] + 'login.py' post_data = { '_login': '******', '_username': name, '_password': password, '_origtarget': 'automation_login.py?_version=%s&_edition_short=%s' % (cmk_version.__version__, cmk_version.edition_short()), '_plain_error': '1', } response = get_url(url, site.get('insecure', False), auth=(name, password), data=post_data).strip() if '<html>' in response.lower(): message = _("Authentication to web service failed.<br>Message:<br>%s") % \ escaping.strip_tags(escaping.strip_scripts(response)) if config.debug: message += "<br>" + _("Automation URL:") + " <tt>%s</tt><br>" % url raise MKAutomationException(message) elif not response: raise MKAutomationException(_("Empty response from web service")) else: try: eval_response = ast.literal_eval(response) except SyntaxError: raise MKAutomationException(response) if isinstance(eval_response, dict): if cmk_version.is_managed_edition( ) and eval_response["edition_short"] != "cme": raise MKUserError( None, _("The Check_MK Managed Services Edition can only " "be connected with other sites using the CME.")) return eval_response["login_secret"] return eval_response
def log_audit(action: str, message: LogMessage, object_ref: Optional[ObjectRef] = None, user_id: Optional[UserId] = None, diff_text: Optional[str] = None) -> None: if config.wato_use_git: if isinstance(message, HTML): message = escaping.strip_tags(message.value) cmk.gui.watolib.git.add_message(message) _log_entry(action, message, object_ref, user_id, diff_text)
def _show_subfolder_hoverarea(self, subfolder): # Only make folder openable when permitted to edit if subfolder.may("read"): html.open_div(class_="hoverarea", onmouseover="cmk.wato.toggle_folder(event, this, true);", onmouseout="cmk.wato.toggle_folder(event, this, false);") self._show_subfolder_buttons(subfolder) html.close_div() # hoverarea else: html.icon("autherr", escaping.strip_tags(subfolder.reason_why_may_not("read")), class_=["autherr"]) html.div('', class_="hoverarea")
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): html.write_text("[\n") html.write(repr([cell.export_title() for cell in cells])) html.write_text(",\n") for row in rows: html.write_text("[") for cell in cells: joined_row = join_row(row, cell) _tdclass, content = cell.render_content(joined_row) html.write(repr(escaping.strip_tags(content))) html.write_text(",") html.write_text("],") html.write_text("\n]\n")
def section(title: Union[None, HTML, str] = None, checkbox: Union[None, HTML, str, Tuple[str, bool, str]] = None, section_id: Optional[str] = None, simple: bool = False, hide: bool = False, legend: bool = True, css: Optional[str] = None, is_show_more: bool = False, is_changed: bool = False, is_required: bool = False) -> None: global g_section_open section_close() html.open_tr( id_=section_id, class_=[ css, "show_more_mode" if is_show_more and not is_changed else "basic" ], style="display:none;" if hide else None, ) if legend: html.open_td(class_=["legend", "simple" if simple else None]) if title: html.open_div( class_=["title", "withcheckbox" if checkbox else None], title=escaping.strip_tags(title)) html.write(escaping.escape_text(title)) html.span('.' * 200, class_=["dots", "required" if is_required else None]) html.close_div() if checkbox: html.open_div(class_="checkbox") if isinstance(checkbox, (str, HTML)): html.write(checkbox) else: name, active, attrname = checkbox html.checkbox( name, active, onclick='cmk.wato.toggle_attribute(this, \'%s\')' % attrname) html.close_div() html.close_td() html.open_td(class_=["content", "simple" if simple else None]) g_section_open = True
def _show_crash_dump_message(crash: 'GUICrashReport', plain_text: bool, fail_silently: bool, show_crash_link: Optional[bool]) -> None: """Create a crash dump from a GUI exception and display a message to the user""" if show_crash_link is None: show_crash_link = config.user.may("general.see_crash_reports") title = _("Internal error") message = u"%s: %s<br>\n<br>\n" % (title, crash.crash_info["exc_value"]) # Do not reveal crash context information to unauthenticated users or not permitted # users to prevent disclosure of internal information if not show_crash_link: message += _( "An internal error occurred while processing your request. " "You can report this issue to your Checkmk administrator. " "Detailed information can be found on the crash report page " "or in <tt>var/log/web.log</tt>.") else: crash_url = makeuri( request, [ ("site", config.omd_site()), ("crash_id", crash.ident_to_text()), ], filename="crash.py", ) message += _( "An internal error occured while processing your request. " "You can report this issue to the Checkmk team to help " "fixing this issue. Please open the <a href=\"%s\">crash report page</a> " "and use the form for reporting the problem.") % crash_url if plain_text: html.set_output_format("text") html.write("%s\n" % escaping.strip_tags(message)) return if fail_silently: return html.header(title, Breadcrumb()) html.show_error(message) html.footer()
def _export_python(view: "View", rows: Rows) -> None: html.write_text("[\n") html.write(repr([cell.export_title() for cell in view.row_cells])) html.write_text(",\n") for row in rows: html.write_text("[") for cell in view.row_cells: joined_row = join_row(row, cell) content = cell.render_for_export(joined_row) # The aggr_treestate painters are returning a dictionary data structure (see # paint_aggregated_tree_state()) in case the output_format is not HTML. Only # remove the HTML tags from the top level strings produced by painters. if isinstance(content, (HTML, str)): content = escaping.strip_tags(content) html.write(repr(content)) html.write_text(",") html.write_text("],") html.write_text("\n]\n")
def render(self, rows, view, group_cells, cells, num_columns, show_checkboxes): html.write_text("[\n") html.write(repr([cell.export_title() for cell in cells])) html.write_text(",\n") for row in rows: html.write_text("[") for cell in cells: joined_row = join_row(row, cell) content = cell.render_for_export(joined_row) # The aggr_treestate painters are returning a dictionary data structure (see # paint_aggregated_tree_state()) in case the output_format is not HTML. Only # remove the HTML tags from the top level strings produced by painters. if isinstance(content, (HTML, six.string_types)): content = escaping.strip_tags(content) html.write(repr(content)) html.write_text(",") html.write_text("],") html.write_text("\n]\n")
def _format_for_csv(self, raw_data): # raw_data can also be int, float content = "%s" % raw_data stripped = escaping.strip_tags(content).replace('\n', '').replace('"', '""') return stripped.encode("utf-8")
def _show_configuration_variables(self, groups): search = self._search at_least_one_painted = False html.open_div(class_="globalvars") for group in sorted(groups, key=lambda g: g.sort_index()): header_is_painted = False # needed for omitting empty groups for config_variable_class in group.config_variables(): config_variable = config_variable_class() varname = config_variable.ident() valuespec = config_variable.valuespec() if not config_variable.domain().enabled(): continue if config_variable.domain( ) == watolib.ConfigDomainCore and varname not in self._default_values: if config.debug: raise MKGeneralException( "The configuration variable <tt>%s</tt> is unknown to " "your local Check_MK installation" % varname) continue if not config_variable.in_global_settings(): continue if self._show_only_modified and varname not in self._current_settings: continue help_text = valuespec.help() or '' title_text = valuespec.title() if search and search not in group.title().lower() \ and search not in config_variable.domain().ident.lower() \ and search not in varname \ and search not in help_text.lower() \ and search not in title_text.lower(): continue # skip variable when search is performed and nothing matches at_least_one_painted = True if not header_is_painted: # always open headers when searching forms.header(group.title(), isopen=search or self._show_only_modified) header_is_painted = True default_value = self._default_values[varname] edit_url = watolib.folder_preserving_link([("mode", self._edit_mode()), ("varname", varname), ("site", html.request.var("site", ""))]) title = html.render_a( title_text, href=edit_url, class_="modified" if varname in self._current_settings else None, title=escaping.strip_tags(help_text)) if varname in self._current_settings: value = self._current_settings[varname] elif varname in self._global_settings: value = self._global_settings[varname] else: value = default_value try: to_text = valuespec.value_to_text(value) except Exception: logger.exception("error converting %r to text", value) to_text = html.render_error(_("Failed to render value: %r") % value) # Is this a simple (single) value or not? change styling in these cases... simple = True if '\n' in to_text or '<td>' in to_text: simple = False forms.section(title, simple=simple) if varname in self._current_settings: modified_cls: Optional[str] = "modified" value_title: Optional[str] = _("This option has been modified.") elif varname in self._global_settings: modified_cls = "modified globally" value_title = _("This option has been modified in global settings.") else: modified_cls = None value_title = None if is_a_checkbox(valuespec): html.open_div(class_=["toggle_switch_container", modified_cls]) html.toggle_switch( enabled=value, help_txt=_("Immediately toggle this setting"), href=html.makeactionuri([("_action", "toggle"), ("_varname", varname)]), class_=modified_cls, title=value_title, ) html.close_div() else: html.a(HTML(to_text), href=edit_url, class_=modified_cls, title=value_title) if header_is_painted: forms.end() if not at_least_one_painted and search: html.show_message(_('Did not find any global setting matching your search.')) html.close_div()
def _show_host_row(self, rendered_hosts, table, hostname, search_text, show_checkboxes, colspan, host_errors, contact_group_names): if search_text and (search_text.lower() not in hostname.lower()): return host = self._folder.host(hostname) rendered_hosts.append(hostname) effective = host.effective_attributes() table.row() # Column with actions (buttons) if show_checkboxes: table.cell(html.render_input( "_toggle_group", type_="button", class_="checkgroup", onclick="cmk.selection.toggle_all_rows();", value='X'), sortable=False, css="checkbox") # Use CSS class "failed" in order to provide information about # selective toggling inventory-failed hosts for Javascript html.input(name="_c_%s" % hostname, type_="checkbox", value=colspan, class_="failed" if host.discovery_failed() else None) html.label("", "_c_%s" % hostname) table.cell(_("Actions"), css="buttons", sortable=False) self._show_host_actions(host) # Hostname with link to details page (edit host) table.cell(_("Hostname")) errors = host_errors.get(hostname, []) + host.validation_errors() if errors: msg = _("Warning: This host has an invalid configuration: ") msg += ", ".join(errors) html.icon(msg, "validation_error") html.nbsp() if host.is_offline(): html.icon(_("This host is disabled"), "disabled") html.nbsp() if host.is_cluster(): html.icon( _("This host is a cluster of %s") % ", ".join(host.cluster_nodes()), "cluster") html.nbsp() html.a(hostname, href=host.edit_url()) # Show attributes for attr in host_attribute_registry.attributes(): if attr.show_in_table(): attrname = attr.name() if attrname in host.attributes(): tdclass, tdcontent = attr.paint( host.attributes()[attrname], hostname) else: tdclass, tdcontent = attr.paint(effective.get(attrname), hostname) tdclass += " inherited" table.cell(attr.title(), escaping.escape_attribute(tdcontent), css=tdclass) # Am I authorized? reason = host.reason_why_may_not("read") if not reason: icon = "authok" title = _("You have permission to this host.") else: icon = "autherr" title = escaping.strip_tags(reason) table.cell(_('Auth'), html.render_icon(icon, title), css="buttons", sortable=False) # Permissions and Contact groups - through complete recursion and inhertance permitted_groups, host_contact_groups, _use_for_services = host.groups( ) table.cell( _("Permissions"), HTML(", ").join([ self._render_contact_group(contact_group_names, g) for g in permitted_groups ])) table.cell( _("Contact Groups"), HTML(", ").join([ self._render_contact_group(contact_group_names, g) for g in host_contact_groups ])) if not config.wato_hide_hosttags: table.cell(_("Tags"), css="tag-ellipsis") tag_groups, show_all_code = self._limit_labels(host.tag_groups()) html.write( cmk.gui.view_utils.render_tag_groups(tag_groups, "host", with_links=False)) html.write(show_all_code) table.cell(_("Explicit labels"), css="tag-ellipsis") labels, show_all_code = self._limit_labels(host.labels()) html.write( cmk.gui.view_utils.render_labels( labels, "host", with_links=False, label_sources={k: "explicit" for k in labels.keys()})) html.write(show_all_code) # Located in folder if self._folder.is_search_folder(): table.cell(_("Folder")) html.a(host.folder().alias_path(), href=host.folder().url())
def _format_for_csv(self, raw_data): # raw_data can also be int, float return escaping.strip_tags(str(raw_data)).replace('\n', '').replace( '"', '""')