def page(self) -> None: assert user.id is not None html.set_render_headfoot(False) html.add_body_css_class("login") html.add_body_css_class("two_factor") html.header(_("Two-factor authentication"), Breadcrumb(), javascripts=[]) html.open_div(id_="login") html.open_div(id_="login_window") html.open_a(href="https://checkmk.com") html.img( src=theme.detect_icon_path(icon_name="logo", prefix="mk-"), id_="logo", class_="custom" if theme.has_custom_logo() else None, ) html.close_a() if not is_two_factor_login_enabled(user.id): raise MKGeneralException(_("Two-factor authentication not enabled")) html.begin_form( "two_factor_login", method="POST", add_transid=False, action="user_login_two_factor.py" ) html.prevent_password_auto_completion() html.hidden_field( "_origtarget", origtarget := request.get_url_input("_origtarget", "index.py") ) if backup_code := request.get_ascii_input("_backup_code"): if is_two_factor_backup_code_valid(user.id, backup_code): set_two_factor_completed() raise HTTPRedirect(origtarget)
def query_limit_exceeded_warn(limit: Optional[int], user_config: LoggedInUser) -> None: """Compare query reply against limits, warn in the GUI about incompleteness""" text = HTML(_("Your query produced more than %d results. ") % limit) if request.get_ascii_input( "limit", "soft") == "soft" and user_config.may("general.ignore_soft_limit"): text += html.render_a( _("Repeat query and allow more results."), target="_self", href=makeuri(request, [("limit", "hard")]), ) elif request.get_ascii_input("limit") == "hard" and user_config.may( "general.ignore_hard_limit"): text += html.render_a( _("Repeat query without limit."), target="_self", href=makeuri(request, [("limit", "none")]), ) text += escaping.escape_to_html_permissive(" " + _( "<b>Note:</b> the shown results are incomplete and do not reflect the sort order." )) html.show_warning(text)
def _from_vars(self): ident = request.get_ascii_input("ident") if ident is not None: try: entry = self._store.filter_editable_entries(self._store.load_for_reading())[ident] except KeyError: raise MKUserError("ident", _("This %s does not exist.") % self._mode_type.name_singular()) self._new = False self._ident: Optional[str] = ident self._entry = entry return clone = request.get_ascii_input("clone") if clone is not None: try: entry = self._store.filter_editable_entries(self._store.load_for_reading())[clone] except KeyError: raise MKUserError("clone", _("This %s does not exist.") % self._mode_type.name_singular()) self._new = True self._ident = None self._entry = copy.deepcopy(entry) return self._new = True self._ident = None self._entry = {}
def _action(self) -> None: assert user.id is not None credentials = load_two_factor_credentials(user.id) if credential_id := request.get_ascii_input("_delete"): if credential_id not in credentials["webauthn_credentials"]: return del credentials["webauthn_credentials"][credential_id] save_two_factor_credentials(user.id, credentials) flash(_("Credential has been deleted"))
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 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 _from_vars(self) -> None: self._name = request.get_ascii_input("edit") # missing -> new group self._new = self._name is None if self._new: clone_group = request.get_ascii_input("clone") if clone_group: self._name = clone_group self.group = self._get_group(self._name) else: self.group = {} else: self.group = self._get_group(self._name) self.group.setdefault("alias", self._name)
def _evaluate_user_opts(self) -> Tuple[TableRows, bool, Optional[str]]: assert self.id is not None table_id = ensure_str(self.id) rows = self.rows search_term = None actions_enabled = (self.options["searchable"] or self.options["sortable"]) if not actions_enabled: return rows, False, None table_opts = user.tableoptions.setdefault(table_id, {}) # Handle the initial visibility of the actions actions_visible = table_opts.get('actions_visible', False) if request.get_ascii_input('_%s_actions' % table_id): actions_visible = request.get_ascii_input('_%s_actions' % table_id) == '1' table_opts['actions_visible'] = actions_visible if self.options["searchable"]: search_term = request.get_unicode_input_mandatory('search', '') # Search is always lower case -> case insensitive search_term = search_term.lower() if search_term: request.set_var('search', search_term) rows = _filter_rows(rows, search_term) if request.get_ascii_input('_%s_reset_sorting' % table_id): request.del_var('_%s_sort' % table_id) if 'sort' in table_opts: del table_opts['sort'] # persist if self.options["sortable"]: # Now apply eventual sorting settings sort = self._get_sort_column(table_opts) if sort is not None: request.set_var('_%s_sort' % table_id, sort) table_opts['sort'] = sort # persist sort_col, sort_reverse = map(int, sort.split(',', 1)) rows = _sort_rows(rows, sort_col, sort_reverse) if actions_enabled: user.save_tableoptions() return rows, actions_visible, search_term
def __init__( self, table_id: Optional[str] = None, title: Optional["HTMLContent"] = None, searchable: bool = True, sortable: bool = True, foldable: Foldable = Foldable.NOT_FOLDABLE, limit: Union[None, int, Literal[False]] = None, output_format: str = "html", omit_if_empty: bool = False, omit_empty_columns: bool = False, omit_headers: bool = False, omit_update_header: bool = False, empty_text: Optional[str] = None, help: Optional[str] = None, # pylint: disable=redefined-builtin css: Optional[str] = None, isopen: bool = True, ): super().__init__() self.next_func = lambda: None self.next_header: Optional[str] = None # Use our pagename as table id if none is specified table_id = table_id if table_id is not None else requested_file_name( request) assert table_id is not None # determine row limit if limit is None: limit = config.table_row_limit if request.get_ascii_input( "limit") == "none" or output_format != "html": limit = None self.id = table_id self.title = title self.rows: TableRows = [] self.limit = limit self.limit_reached = False self.limit_hint: Optional[int] = None self.headers: List[TableHeader] = [] self.options = { "collect_headers": False, # also: True, "finished" "omit_if_empty": omit_if_empty, "omit_empty_columns": omit_empty_columns, "omit_headers": omit_headers, "omit_update_header": omit_update_header, "searchable": searchable, "sortable": sortable, "foldable": foldable, "output_format": output_format, # possible: html, csv, fetch } self.empty_text = empty_text if empty_text is not None else _( "No entries.") self.help = help self.css = css self.mode = "row" self.isopen: Final = isopen
def edit_dictionaries(dictionaries: 'Sequence[Tuple[str, Union[Transform, Dictionary]]]', value: Dict[str, Any], focus: Optional[str] = None, hover_help: bool = True, validate: Optional[Callable[[Any], None]] = None, title: Optional[str] = None, method: str = "GET", preview: bool = False, varprefix: str = "", formname: str = "form", consume_transid: bool = True): if request.get_ascii_input("filled_in") == formname and transactions.transaction_valid(): if not preview and consume_transid: transactions.check_transaction() messages: List[str] = [] new_value: Dict[str, Dict[str, Any]] = {} for keyname, vs_dict in dictionaries: dict_varprefix = varprefix + keyname new_value[keyname] = {} try: edited_value = vs_dict.from_html_vars(dict_varprefix) vs_dict.validate_value(edited_value, dict_varprefix) new_value[keyname].update(edited_value) except MKUserError as e: messages.append("%s: %s" % (vs_dict.title() or _("Properties"), e)) user_errors.add(e) except Exception as e: messages.append("%s: %s" % (vs_dict.title() or _("Properties"), e)) user_errors.add(MKUserError(None, str(e))) if validate and not user_errors: try: validate(new_value[keyname]) except MKUserError as e: messages.append(str(e)) user_errors.add(e) if messages: messages_joined = "".join(["%s<br>\n" % m for m in messages]) if not preview: html.show_error(messages_joined) else: raise MKUserError(None, messages_joined) else: return new_value html.begin_form(formname, method=method) for keyname, vs_dict in dictionaries: dict_varprefix = varprefix + keyname subvalue = value.get(keyname, {}) vs_dict.render_input_as_form(dict_varprefix, subvalue) end() # Should be ignored be hidden_fields, but I do not dare to change it there request.del_var("filled_in") html.hidden_fields() html.end_form()
def _from_vars(self) -> None: edit_group = request.get_ascii_input("edit") # missing -> new group self._name = GroupName(edit_group) if edit_group else None self._new = self._name is None if self._new: clone_group = request.get_ascii_input("clone") if clone_group: self._name = GroupName(clone_group) self.group = self._get_group(self._name) else: self.group = {} else: assert self._name is not None self.group = self._get_group(self._name) self.group.setdefault("alias", self._name)
def get_request(self) -> FetchAgentOutputRequest: user.need_permission("wato.download_agent_output") ascii_input = request.get_ascii_input("request") if ascii_input is None: raise MKUserError("request", _('The parameter "%s" is missing.') % "request") return FetchAgentOutputRequest.deserialize( ast.literal_eval(ascii_input))
def _init_host(self) -> watolib.CREHost: clonename = request.get_ascii_input("clone") if not clonename: return self._init_new_host_object() if not watolib.Folder.current().has_host(clonename): raise MKUserError("host", _("You called this page with an invalid host name.")) if not user.may("wato.clone_hosts"): raise MKAuthException(_("Sorry, you are not allowed to clone hosts.")) host = watolib.Folder.current().load_host(clonename) self._verify_host_type(host) return host
def _breadcrumb_item(self) -> BreadcrumbItem: """Return the breadcrumb item for the current mode""" # For the currently active mode use the same link as the "page title click" if request.get_ascii_input("mode") == self.name(): breadcrumb_url = "javascript:window.location.reload(false)" else: breadcrumb_url = self._breadcrumb_url() return BreadcrumbItem( title=self.title(), url=breadcrumb_url, )
def _localize_request() -> None: previous_language = cmk.gui.i18n.get_current_language() user_language = request.get_ascii_input("lang", user.language) set_language_cookie(request, response, user_language) cmk.gui.i18n.localize(user_language) # All plugins might have to be reloaded due to a language change. Only trigger # a second plugin loading when the user is really using a custom localized GUI. # Otherwise the load_all_plugins() at the beginning of the request is sufficient. if cmk.gui.i18n.get_current_language() != previous_language: load_all_plugins()
def _show_form(self) -> None: assert user.id is not None users = userdb.load_users() change_reason = request.get_ascii_input("reason") if change_reason == "expired": html.p( _("Your password is too old, you need to choose a new password." )) elif change_reason == "enforced": html.p( _("You are required to change your password before proceeding." )) user_spec = users.get(user.id) if user_spec is None: html.show_warning(_("Sorry, your user account does not exist.")) html.footer() return locked_attributes = userdb.locked_attributes( user_spec.get("connector")) if "password" in locked_attributes: raise MKUserError( "cur_password", _("You can not change your password, because it is " "managed by another system."), ) html.begin_form("profile", method="POST") html.prevent_password_auto_completion() html.open_div(class_="wato") forms.header(self._page_title()) forms.section(_("Current Password")) html.password_input("cur_password", autocomplete="new-password") forms.section(_("New Password")) html.password_input("password", autocomplete="new-password") forms.section(_("New Password Confirmation")) html.password_input("password2", autocomplete="new-password") forms.end() html.close_div() html.hidden_fields() html.end_form() html.footer()
def _from_vars(self): self._connection_id = request.get_ascii_input("id") self._connection_cfg = {} self._connections = load_connection_config(lock=transactions.is_transaction()) if self._connection_id is None: clone_id = request.var("clone") if clone_id is not None: self._connection_cfg = self._get_connection_cfg_and_index(clone_id)[0] self._new = True return self._new = False self._connection_cfg, self._connection_nr = self._get_connection_cfg_and_index( self._connection_id )
def action(self) -> ActionResult: if not transactions.transaction_valid(): return None action_var = request.get_str_input("_action") if action_var is None: return None if action_var != "delete": return self._handle_custom_action(action_var) if not transactions.check_transaction(): return redirect(mode_url(self._mode_type.list_mode_name())) entries = self._store.load_for_modification() ident = request.get_ascii_input("_delete") if ident not in entries: raise MKUserError( "_delete", _("This %s does not exist.") % self._mode_type.name_singular()) if ident not in self._store.filter_editable_entries(entries): raise MKUserError( "_delete", _("You are not allowed to delete this %s.") % self._mode_type.name_singular(), ) self._validate_deletion(ident, entries[ident]) entry = entries.pop(ident) self._add_change( action="delete", text=_("Removed the %s '%s'") % (self._mode_type.name_singular(), ident), affected_sites=self._mode_type.affected_sites(entry), ) self._store.save(entries) flash(_("The %s has been deleted.") % self._mode_type.name_singular()) return redirect(mode_url(self._mode_type.list_mode_name()))
def _from_vars(self): self._name = request.get_ascii_input( "edit") # missing -> new custom attr self._new = self._name is None # TODO: Inappropriate Intimacy: custom host attributes should not now about # custom user attributes and vice versa. The only reason they now about # each other now is that they are stored in one file. self._all_attrs = load_custom_attrs_from_mk_file( lock=transactions.is_transaction()) if not self._new: matching_attrs = [ a for a in self._attrs if a["name"] == self._name ] if not matching_attrs: raise MKUserError(None, _("The attribute does not exist.")) self._attr: Dict[str, Any] = matching_attrs[0] else: self._attr = {}
def _from_vars(self) -> None: self._checkmk_config_files_map = get_checkmk_config_files_map() self._checkmk_log_files_map = get_checkmk_log_files_map() self._collect_dump = bool(request.get_ascii_input("_collect_dump")) self._diagnostics_parameters = self._get_diagnostics_parameters() self._job = DiagnosticsDumpBackgroundJob()
def _render_headers(self, actions_enabled: bool, actions_visible: bool, empty_columns: List[bool]) -> None: if self.options["omit_headers"]: return table_id = self.id html.open_tr() first_col = True for nr, header in enumerate(self.headers): if self.options["omit_empty_columns"] and empty_columns[nr]: continue if header.help_txt: header_title: HTML = html.render_span(header.title, title=header.help_txt) else: header_title = header.title if not isinstance(header.css, list): css_class: "CSSSpec" = [header.css] else: css_class = header.css assert isinstance(css_class, list) css_class = [("header_%s" % c) for c in css_class if c is not None] if not self.options["sortable"] or not header.sortable: html.open_th(class_=css_class) else: css_class.insert(0, "sort") reverse = 0 sort = request.get_ascii_input("_%s_sort" % table_id) if sort: sort_col, sort_reverse = map(int, sort.split(",", 1)) if sort_col == nr: reverse = 1 if sort_reverse == 0 else 0 action_uri = makeactionuri(request, transactions, [("_%s_sort" % table_id, "%d,%d" % (nr, reverse))]) html.open_th( class_=css_class, title=_("Sort by %s") % header.title, onclick="location.href='%s'" % action_uri, ) # Add the table action link if first_col: first_col = False if actions_enabled: if not header_title: header_title = HTML( " " ) # Fixes layout problem with white triangle if actions_visible: state = "0" help_txt = _("Hide table actions") img = "table_actions_on" else: state = "1" help_txt = _("Display table actions") img = "table_actions_off" html.open_div(class_=["toggle_actions"]) html.icon_button( makeuri(request, [("_%s_actions" % table_id, state)]), help_txt, img, cssclass="toggle_actions", ) html.span(header_title) html.close_div() else: html.write_text(header_title) else: html.write_text(header_title) html.close_th() html.close_tr()
def _get_sort_column(self, table_opts: Dict[str, Any]) -> Optional[str]: return request.get_ascii_input("_%s_sort" % self.id, table_opts.get("sort"))
def _localize_request() -> None: user_language = request.get_ascii_input("lang", user.language) set_language_cookie(request, response, user_language) cmk.gui.i18n.localize(user_language)