예제 #1
0
    def get_request(self):
        site_id = request.get_ascii_input_mandatory("site_id")
        cmk.gui.watolib.activate_changes.verify_remote_site_config(site_id)

        try:
            domains = ast.literal_eval(request.get_ascii_input_mandatory("domains"))
        except SyntaxError:
            raise watolib.MKAutomationException(
                _("Invalid request: %r") % request.get_ascii_input_mandatory("domains"))

        return ActivateChangesRequest(site_id=site_id, domains=domains)
예제 #2
0
    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),
                }
            )
        )
예제 #3
0
    def page(self) -> None:
        if not user.may("wato.diagnostics"):
            raise MKAuthException(
                _("Sorry, you lack the permission for downloading diagnostics dumps.")
            )

        site = 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)
예제 #4
0
    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()
예제 #5
0
    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())
예제 #6
0
    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 = forms.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)

        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)
예제 #7
0
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)))
예제 #8
0
파일: roles.py 프로젝트: gradecke/checkmk
    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."))
예제 #9
0
 def page(self):
     html.div(html.render_message(_("Loading...")), id_="async_progress_msg")
     html.div("", id_="status_container")
     html.javascript(
         "cmk.background_job.start('ajax_background_job_details.py', %s)"
         % json.dumps(request.get_ascii_input_mandatory("job_id"))
     )
예제 #10
0
    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)
예제 #11
0
    def _from_vars(self):
        self._hostname = request.get_ascii_input_mandatory("host")
        self._host = watolib.Folder.current().host(self._hostname)
        self._host.need_permission("read")

        if self._host.is_cluster():
            raise MKGeneralException(_("This page does not support cluster hosts."))
예제 #12
0
    def _action(self) -> None:
        assert config.user.id is not None

        users = userdb.load_users(lock=True)
        user = users[config.user.id]

        language = request.get_ascii_input_mandatory('language', "")
        # Set the users language if requested to set it explicitly
        if language != "_default_":
            user['language'] = language
            config.user.language = language
            set_language_cookie(request, response, language)

        else:
            if 'language' in user:
                del user['language']
            config.user.reset_language()

        # load the new language
        cmk.gui.i18n.localize(config.user.language)

        if config.user.may('general.edit_notifications') and user.get(
                "notifications_enabled"):
            value = forms.get_input(watolib.get_vs_flexible_notifications(),
                                    "notification_method")
            user["notification_method"] = value

        # Custom attributes
        if config.user.may('general.edit_user_attributes'):
            for name, attr in userdb.get_user_attributes():
                if not attr.user_editable():
                    continue

                if attr.permission() and not config.user.may(
                        attr.permission()):
                    continue

                vs = attr.valuespec()
                value = vs.from_html_vars('ua_' + name)
                vs.validate_value(value, "ua_" + name)
                user[name] = value

        userdb.save_users(users)

        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 config.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)
예제 #13
0
 def _init_host(self) -> watolib.CREHost:
     hostname = request.get_ascii_input_mandatory("host")
     folder = watolib.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
예제 #14
0
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))
예제 #15
0
    def _from_vars(self):
        self._hostname = request.get_ascii_input_mandatory("host")
        self._host = watolib.Folder.current().host(self._hostname)
        if self._host is None:
            raise MKUserError("host", _("The given host does not exist."))
        self._host.need_permission("read")

        # TODO: Validate?
        self._service = request.get_unicode_input("service")
예제 #16
0
    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 watolib.MKAutomationException(
                _("Invalid request: %r") % request.get_ascii_input_mandatory("domains")
            )

        return ActivateChangesRequest(site_id=site_id, domains=serialized_domain_requests)
예제 #17
0
파일: sites.py 프로젝트: bbaumer/checkmk
    def get_request(self) -> PushSnapshotRequest:
        site_id = request.get_ascii_input_mandatory("siteid")
        cmk.gui.watolib.activate_changes.verify_remote_site_config(site_id)

        snapshot = request.uploaded_file("snapshot")
        if not snapshot:
            raise MKGeneralException(_('Invalid call: The snapshot is missing.'))

        return PushSnapshotRequest(site_id=site_id, tar_content=ensure_binary(snapshot[2]))
예제 #18
0
    def _from_vars(self):
        host_name = request.get_ascii_input_mandatory("host")

        if not watolib.Folder.current().has_host(host_name):
            raise MKUserError("host", _("You called this page with an invalid host name."))

        if not config.user.may("wato.rename_hosts"):
            raise MKAuthException(_("You don't have the right to rename hosts"))

        self._host = watolib.Folder.current().host(host_name)
        self._host.need_permission("write")
예제 #19
0
    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_unicode_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()
예제 #20
0
    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 = 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()
예제 #21
0
    def action(self) -> ActionResult:
        if not transactions.transaction_valid():
            return redirect(mode_url("folder"))

        attributes = watolib.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 = watolib.Folder.current()

        if transactions.check_transaction():
            folder.create_hosts([(hostname, attributes, cluster_nodes)])

        self._host = folder.load_host(hostname)

        inventory_url = watolib.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()))
예제 #22
0
    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 = watolib.load_configuration_settings()
        self._global_settings = {}
예제 #23
0
파일: groups.py 프로젝트: petrows/checkmk
    def action(self) -> ActionResult:
        if not transactions.check_transaction():
            return redirect(mode_url("%s_groups" % self.type_name))

        alias = request.get_unicode_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()
            watolib.add_group(self._name, self.type_name, self.group)
        else:
            watolib.edit_group(self._name, self.type_name, self.group)

        return redirect(mode_url("%s_groups" % self.type_name))
예제 #24
0
파일: groups.py 프로젝트: petrows/checkmk
    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 = watolib.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)

            watolib.delete_group(delname, self.type_name)
            self._groups = self._load_groups()

        return redirect(mode_url("%s_groups" % self.type_name))
예제 #25
0
    def _from_vars(self):
        self._check_type = request.get_ascii_input_mandatory("check_type", "")

        builtin_check_types = ["check-mk", "check-mk-inventory"]
        if (not re.match("^[a-zA-Z0-9_.]+$", self._check_type)
                and self._check_type not in builtin_check_types):
            raise MKUserError("check_type", _("Invalid check type"))

        manpage = man_pages.load_man_page(self._check_type)
        if manpage is None:
            raise MKUserError(None, _("There is no manpage for this check."))
        self._manpage = manpage

        checks = get_check_information().plugin_infos
        if self._check_type in checks:
            self._manpage = {
                "type": "check_mk",
                **checks[self._check_type],
                **self._manpage,
            }
        elif self._check_type.startswith("check_"):  # Assume active check
            self._manpage = {
                "type": "active",
                **self._manpage,
            }
        elif self._check_type in builtin_check_types:
            self._manpage = {
                "type":
                "check_mk",
                "service_description":
                "Check_MK%s" %
                ("" if self._check_type == "check-mk" else " Discovery"),
                **self._manpage,
            }
        else:
            raise MKUserError(
                None,
                _("Could not detect type of manpage: %s. Maybe the check is missing "
                  ) % self._check_type,
            )
예제 #26
0
    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.man_page_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])
예제 #27
0
 def get_request(self) -> str:
     return request.get_ascii_input_mandatory("tarfile_name")
예제 #28
0
 def get_request(self) -> str:
     return ast.literal_eval(request.get_ascii_input_mandatory("request"))
예제 #29
0
 def get_request(self) -> CheckmkAutomationRequest:
     return CheckmkAutomationRequest(
         *ast.literal_eval(request.get_ascii_input_mandatory("request")))
예제 #30
0
파일: roles.py 프로젝트: gradecke/checkmk
    def action(self) -> ActionResult:
        if html.form_submitted("search"):
            return None

        alias = request.get_unicode_input_mandatory("alias")

        unique, info = watolib.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()
        watolib.add_change("edit-roles",
                           _("Modified user role '%s'") % new_id,
                           sites=get_login_sites())
        return redirect(mode_url("roles"))