def handle_acknowledgement(): if html.request.var("_werk_ack") and html.check_transaction(): werk_id = html.request.get_integer_input_mandatory("_werk_ack") if werk_id not in g_werks: raise MKUserError("werk", _("This werk does not exist.")) werk = g_werks[werk_id] if werk["compatible"] == "incomp_unack": acknowledge_werk(werk) html.show_message( _("Werk %s - %s has been acknowledged.") % (render_werk_id( werk, with_link=True), render_werk_title(werk))) html.reload_sidebar() load_werks() # reload ack states after modification elif html.request.var("_ack_all"): if html.confirm(_( "Do you really want to acknowledge <b>all</b> incompatible werks?" ), method="GET"): num = len(unacknowledged_incompatible_werks()) acknowledge_all_werks() html.show_message( _("%d incompatible Werks have been acknowledged.") % num) html.reload_sidebar() load_werks() # reload ack states after modification render_unacknowleged_werks()
def _show_form(self, profile_changed: bool) -> None: assert config.user.id is not None users = userdb.load_users() if profile_changed: html.reload_sidebar() html.show_message(_("Successfully updated user profile.")) # Ensure theme changes are applied without additional user interaction html.immediate_browser_redirect(0.5, makeuri(global_request, [])) if html.has_user_errors(): html.show_user_errors() user = users.get(config.user.id) if user is None: html.show_warning(_("Sorry, your user account does not exist.")) html.footer() return html.begin_form("profile", method="POST") html.prevent_password_auto_completion() html.open_div(class_="wato") forms.header(self._page_title()) forms.section(_("Name"), simple=True) html.write_text(user.get("alias", config.user.id)) select_language(user) # Let the user configure how he wants to be notified rulebased_notifications = rulebased_notifications_enabled() if (not rulebased_notifications and config.user.may('general.edit_notifications') and user.get("notifications_enabled")): forms.section(_("Notifications")) html.help( _("Here you can configure how you want to be notified about host and service problems and " "other monitoring events.")) watolib.get_vs_flexible_notifications().render_input( "notification_method", user.get("notification_method")) if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if attr.user_editable(): vs = attr.valuespec() forms.section(_u(vs.title())) value = user.get(name, vs.default_value()) if not attr.permission() or config.user.may( attr.permission()): vs.render_input("ua_" + name, value) html.help(_u(vs.help())) else: html.write(vs.value_to_text(value)) forms.end() html.close_div() html.hidden_fields() html.end_form() html.footer()
def page_clear(): acktime = html.request.get_float_input_mandatory('acktime', time.time()) prev_url = html.get_url_input('prev_url', '') if html.request.var('_confirm'): acknowledge_failed_notifications(acktime) if config.user.authorized_login_sites(): # This local import is needed for the moment import cmk.gui.wato.user_profile # pylint: disable=redefined-outer-name cmk.gui.wato.user_profile.user_profile_async_replication_page() return failed_notifications = load_failed_notifications(before=acktime, after=acknowledged_time()) render_page_confirm(acktime, prev_url, failed_notifications) if html.request.var('_confirm'): html.reload_sidebar()
def show(self): # type: () -> None if not config.user.may("general.configure_sidebar"): raise MKGeneralException(_("You are not allowed to change the sidebar.")) html.header(_("Available snapins")) html.begin_context_buttons() CustomSnapins.context_button_list() html.end_context_buttons() addname = html.request.var("name") if addname in snapin_registry and addname not in self._used_snapins( ) and html.check_transaction(): self._user_config.add_snapin(UserSidebarSnapin.from_snapin_type_id(addname)) self._user_config.save() html.reload_sidebar() self._show_builtin_snapins()
def _bulk_delete_after_confirm(cls, what): to_delete = [] for varname, _value in html.request.itervars(prefix="_c_%s+" % what): if html.get_checkbox(varname): checkbox_ident = varname.split("_c_%s+" % what)[-1] to_delete.append(checkbox_ident.split("+", 1)) if not to_delete: return c = html.confirm( _("Do you really want to delete %d %s?") % (len(to_delete), cls.phrase("title_plural"))) if c: for owner, instance_id in to_delete: cls.remove_instance((owner, instance_id)) for owner in {e[0] for e in to_delete}: cls.save_user_instances(owner) html.reload_sidebar() elif c is False: return False
def _show_page_user_profile(change_pw): start_async_replication = False if not config.user.id: raise MKUserError(None, _('Not logged in.')) if not config.user.may('general.edit_profile') and not config.user.may( 'general.change_password'): raise MKAuthException( _("You are not allowed to edit your user profile.")) if not config.wato_enabled: raise MKAuthException( _('User profiles can not be edited (WATO is disabled).')) success = None if html.request.has_var('_save') and html.check_transaction(): users = userdb.load_users(lock=True) try: # Profile edit (user options like language etc.) if config.user.may('general.edit_profile'): if not change_pw: set_lang = html.get_checkbox('_set_lang') language = html.request.var('language') # Set the users language if requested if set_lang: if language == '': language = None # Set custom language users[config.user.id]['language'] = language config.user.language = language html.set_language_cookie(language) else: # Remove the customized language if 'language' in users[config.user.id]: del users[config.user.id]['language'] config.user.reset_language() # load the new language cmk.gui.i18n.localize(config.user.language) user = users.get(config.user.id) if config.user.may('general.edit_notifications' ) and user.get("notifications_enabled"): value = forms.get_input( watolib.get_vs_flexible_notifications(), "notification_method") users[config.user.id]["notification_method"] = value # Custom attributes if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if attr.user_editable(): if not attr.permission() or config.user.may( attr.permission()): vs = attr.valuespec() value = vs.from_html_vars('ua_' + name) vs.validate_value(value, "ua_" + name) users[config.user.id][name] = value # Change the password if requested password_changed = False if config.user.may('general.change_password'): cur_password = html.request.var('cur_password') password = html.request.var('password') password2 = html.request.var('password2', '') if change_pw: # Force change pw mode if not cur_password: raise MKUserError( "cur_password", _("You need to provide your current password.")) if not password: raise MKUserError( "password", _("You need to change your password.")) if cur_password == password: raise MKUserError( "password", _("The new password must differ from your current one." )) if cur_password and password: if userdb.hook_login(config.user.id, cur_password) is False: raise MKUserError("cur_password", _("Your old password is wrong.")) if password2 and password != password2: raise MKUserError( "password2", _("The both new passwords do not match.")) watolib.verify_password_policy(password) users[config.user.id]['password'] = hash_password(password) users[config.user.id]['last_pw_change'] = int(time.time()) if change_pw: # Has been changed, remove enforcement flag del users[config.user.id]['enforce_pw_change'] # Increase serial to invalidate old cookies if 'serial' not in users[config.user.id]: users[config.user.id]['serial'] = 1 else: users[config.user.id]['serial'] += 1 password_changed = True # Now, if in distributed environment where users can login to remote sites, # set the trigger for pushing the new auth information to the slave sites # asynchronous if config.user.authorized_login_sites(): start_async_replication = True userdb.save_users(users) if password_changed: # Set the new cookie to prevent logout for the current user login.set_auth_cookie(config.user.id) success = True except MKUserError as e: html.add_user_error(e.varname, e) else: users = userdb.load_users() watolib.init_wato_datastructures(with_wato_lock=True) # When in distributed setup, display the replication dialog instead of the normal # profile edit dialog after changing the password. if start_async_replication: user_profile_async_replication_page() return if change_pw: title = _("Change Password") else: title = _("Edit User Profile") html.header(title) # Rule based notifications: The user currently cannot simply call the according # WATO module due to WATO permission issues. So we cannot show this button # right now. if not change_pw: rulebased_notifications = watolib.load_configuration_settings().get( "enable_rulebased_notifications") if rulebased_notifications and config.user.may( 'general.edit_notifications'): html.begin_context_buttons() url = "wato.py?mode=user_notifications_p" html.context_button(_("Notifications"), url, "notifications") html.end_context_buttons() else: reason = html.request.var('reason') if reason == 'expired': html.p( _('Your password is too old, you need to choose a new password.' )) else: html.p( _('You are required to change your password before proceeding.' )) if success: html.reload_sidebar() if change_pw: html.show_message(_("Your password has been changed.")) raise HTTPRedirect(html.request.var('_origtarget', 'index.py')) else: html.show_message(_("Successfully updated user profile.")) # Ensure theme changes are applied without additional user interaction html.immediate_browser_redirect(0.5, html.makeuri([])) if html.has_user_errors(): html.show_user_errors() user = users.get(config.user.id) if user is None: html.show_warning(_("Sorry, your user account does not exist.")) html.footer() return # Returns true if an attribute is locked and should be read only. Is only # checked when modifying an existing user locked_attributes = userdb.locked_attributes(user.get('connector')) def is_locked(attr): return attr in locked_attributes html.begin_form("profile", method="POST") html.prevent_password_auto_completion() html.open_div(class_="wato") forms.header(_("Personal Settings")) if not change_pw: forms.section(_("Name"), simple=True) html.write_text(user.get("alias", config.user.id)) if config.user.may( 'general.change_password') and not is_locked('password'): forms.section(_("Current Password")) html.password_input('cur_password', autocomplete="new-password") forms.section(_("New Password")) html.password_input('password', autocomplete="new-password") forms.section(_("New Password Confirmation")) html.password_input('password2', autocomplete="new-password") if not change_pw and config.user.may('general.edit_profile'): select_language(user) # Let the user configure how he wants to be notified if not rulebased_notifications \ and config.user.may('general.edit_notifications') \ and user.get("notifications_enabled"): forms.section(_("Notifications")) html.help( _("Here you can configure how you want to be notified about host and service problems and " "other monitoring events.")) watolib.get_vs_flexible_notifications().render_input( "notification_method", user.get("notification_method")) if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if attr.user_editable(): vs = attr.valuespec() forms.section(_u(vs.title())) value = user.get(name, vs.default_value()) if not attr.permission() or config.user.may( attr.permission()): vs.render_input("ua_" + name, value) html.help(_u(vs.help())) else: html.write(vs.value_to_text(value)) # Save button forms.end() html.button("_save", _("Save")) html.close_div() html.hidden_fields() html.end_form() html.footer()
def 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 # noqa: F401 # pylint: disable=unused-variable,redefined-outer-name 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 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() return
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() 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 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 _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) 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, 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() # 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: html.add_user_error(e.varname, str(e)) except MKAuthException as e: reason = e.args[0] html.add_user_error(None, reason) 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 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 failed action on this page if html.has_user_errors(): 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_sidebar() if config.wato_use_git and html.is_transaction(): do_git_commit() wato_html_footer(show_body_end=display_options.enabled(display_options.H))