Exemple #1
0
 def _check_customer(self, request):
     if cmk.is_managed_edition():
         if "customer" not in request.keys():
             raise MKUserError(None, _("Missing required key(s): %s") % "customer")
     else:
         if "customer" in request.keys():
             raise MKUserError(None, _("Invalid key(s): %s") % "customer")
Exemple #2
0
def edit_group(name, group_type, extra_info):
    _check_modify_group_permissions(group_type)
    all_groups = _load_group_information()
    groups = all_groups.get(group_type, {})

    if name not in groups:
        raise MKUserError("name", _("Unknown group: %s") % name)

    old_group_backup = copy.deepcopy(groups[name])

    _set_group(all_groups, group_type, name, extra_info)
    if cmk.is_managed_edition():
        old_customer = managed.get_customer_id(old_group_backup)
        new_customer = managed.get_customer_id(extra_info)
        if old_customer != new_customer:
            _add_group_change(
                old_group_backup, "edit-%sgroups" % group_type,
                _("Removed %sgroup %s from customer %s") %
                (group_type, name,
                 managed.get_customer_name_by_id(old_customer)))
            _add_group_change(
                extra_info, "edit-%sgroups" % group_type,
                _("Moved %sgroup %s to customer %s. Additional properties may have changed."
                  ) % (group_type, name,
                       managed.get_customer_name_by_id(new_customer)))
        else:
            _add_group_change(
                old_group_backup, "edit-%sgroups" % group_type,
                _("Updated properties of %sgroup %s") % (group_type, name))
    else:
        _add_group_change(
            extra_info, "edit-%sgroups" % group_type,
            _("Updated properties of %s group %s") % (group_type, name))
Exemple #3
0
def _add_group_change(group, action_name, text):
    group_sites = None
    if cmk.is_managed_edition() and not managed.is_global(
            managed.get_customer_id(group)):
        group_sites = managed.get_sites_of_customer(
            managed.get_customer_id(group))

    add_change(action_name, text, sites=group_sites)
Exemple #4
0
def _connect_multiple_sites(user):
    # type: (UserType) -> None
    enabled_sites, disabled_sites = _get_enabled_and_disabled_sites(user)
    _set_initial_site_states(enabled_sites, disabled_sites)

    if is_managed_edition():
        import cmk.gui.cme.managed as managed  # pylint: disable=no-name-in-module
        g.live = managed.CMEMultiSiteConnection(enabled_sites, disabled_sites)
    else:
        g.live = MultiSiteConnection(enabled_sites, disabled_sites)

    # Fetch status of sites by querying the version of Nagios and livestatus
    # This may be cached by a proxy for up to the next configuration reload.
    g.live.set_prepend_site(True)
    for response in g.live.query(
            "GET status\n"
            "Cache: reload\n"
            "Columns: livestatus_version program_version program_start num_hosts num_services"
    ):

        try:
            site_id, v1, v2, ps, num_hosts, num_services = response
        except ValueError:
            e = MKLivestatusQueryError("Invalid response to status query: %s" %
                                       response)

            site_id = response[0]
            g.site_status[site_id].update({
                "exception":
                e,
                "status_host_state":
                None,
                "state":
                _status_host_state_name(None),
            })
            continue

        g.site_status[site_id].update({
            "state":
            "online",
            "livestatus_version":
            v1,
            "program_version":
            v2,
            "program_start":
            ps,
            "num_hosts":
            num_hosts,
            "num_services":
            num_services,
            "core":
            v2.startswith("Check_MK") and "cmc" or "nagios",
        })
    g.live.set_prepend_site(False)

    # TODO(lm): Find a better way to make the Livestatus object trigger the update
    # once self.deadsites is updated.
    update_site_states_from_dead_sites()
Exemple #5
0
    def _get_check_mk_edition_title(self):
        if cmk.is_enterprise_edition():
            if cmk.is_demo():
                return "Enterprise (Demo)"
            return "Enterprise"

        elif cmk.is_managed_edition():
            return "Managed"

        return "Raw"
Exemple #6
0
    def _get_group_extra_info(self, group_type, request):
        extra_info = {}
        extra_info["alias"] = request.get("alias")

        if group_type == "contact" and "nagvis_maps" in request:
            extra_info["nagvis_maps"] = request["nagvis_maps"]

        if cmk.is_managed_edition():
            extra_info["customer"] = request["customer"]

        return extra_info
Exemple #7
0
    def __init__(self):
        super(ModeEditUser, self).__init__()

        # Load data that is referenced - in order to display dropdown
        # boxes and to check for validity.
        self._contact_groups = load_contact_group_information()
        self._timeperiods = watolib.timeperiods.load_timeperiods()
        self._roles = userdb.load_roles()

        if cmk.is_managed_edition():
            self._vs_customer = managed.vs_customer()
Exemple #8
0
    def buttons(self):
        global_buttons()

        if config.user.may("wato.set_read_only"):
            html.context_button(
                _("Read only mode"),
                watolib.folder_preserving_link([("mode", "read_only")]),
                "read_only")

        if cmk.is_managed_edition():
            cmk.gui.cme.plugins.wato.managed.cme_global_settings_buttons()
Exemple #9
0
def test_delete_group(web, group_type):
    group_id = "%s_testgroup_id" % group_type
    group_alias = "%s_testgroup_alias" % group_type
    try:
        attributes = {"alias": group_alias}

        if cmk.is_managed_edition():
            attributes["customer"] = "provider"

        web.add_group(group_type, group_id, attributes)
    finally:
        web.delete_group(group_type, group_id)
Exemple #10
0
def test_add_group(web, group_type):
    group_id = "%s_testgroup_id" % group_type
    group_alias = "%s_testgroup_alias" % group_type
    try:
        attributes = {"alias": group_alias}

        if cmk.is_managed_edition():
            attributes["customer"] = "provider"

        web.add_group(group_type, group_id, attributes)
        all_groups = web.get_all_groups(group_type)

        assert group_id in all_groups
        assert group_alias == all_groups[group_id]["alias"]

        if cmk.is_managed_edition():
            assert all_groups[group_id]["provider"] == "provider"
    finally:
        all_groups = web.get_all_groups(group_type)
        if group_id in all_groups:
            web.delete_group(group_type, group_id)
Exemple #11
0
def get_check_mk_edition_title():
    import cmk
    if cmk.is_enterprise_edition():
        if cmk.is_demo():
            return "Enterprise (Demo)"
        else:
            return "Enterprise"

    elif cmk.is_managed_edition():
        return "Managed"

    else:
        return "Raw"
Exemple #12
0
def _connect_multiple_sites():
    global _live
    enabled_sites, disabled_sites = _get_enabled_and_disabled_sites()
    _set_initial_site_states(enabled_sites, disabled_sites)

    if cmk.is_managed_edition():
        import managed
        _live = managed.CMEMultiSiteConnection(enabled_sites, disabled_sites)
    else:
        _live = livestatus.MultiSiteConnection(enabled_sites, disabled_sites)

    # Fetch status of sites by querying the version of Nagios and livestatus
    # This may be cached by a proxy for up to the next configuration reload.
    _live.set_prepend_site(True)
    for response in _live.query(
            "GET status\n"
            "Cache: reload\n"
            "Columns: livestatus_version program_version program_start num_hosts num_services"
    ):

        site_id, v1, v2, ps, num_hosts, num_services = response

        _update_site_status(
            site_id, {
                "state": "online",
                "livestatus_version": v1,
                "program_version": v2,
                "program_start": ps,
                "num_hosts": num_hosts,
                "num_services": num_services,
                "core": v2.startswith("Check_MK") and "cmc" or "nagios",
            })
    _live.set_prepend_site(False)

    # Get exceptions in case of dead sites
    for site_id, deadinfo in _live.dead_sites().items():
        shs = deadinfo.get("status_host_state")
        _update_site_status(
            site_id, {
                "exception": deadinfo["exception"],
                "status_host_state": shs,
                "state": _status_host_state_name(shs),
            })
Exemple #13
0
def test_edit_group_missing(web, group_type):
    group_id = "%s_testgroup_id" % group_type
    group_alias = "%s_testgroup_alias" % group_type
    group_alias2 = "%s_testgroup_otheralias" % group_type
    try:
        attributes = {"alias": group_alias}

        if cmk.is_managed_edition():
            attributes["customer"] = "provider"

        web.add_group(group_type, group_id, attributes)
        try:
            #web.edit_group(group_type, group_id, {"alias": group_alias2}, expect_error = True)
            web.edit_group(group_type,
                           "%s_missing" % group_id, {"alias": group_alias2},
                           expect_error=True)
        except APIError as e:
            assert str(e) != str(None)
            return

        assert False
    finally:
        web.delete_group(group_type, group_id)
Exemple #14
0
def test_edit_cg_group_with_nagvis_maps(web, site):
    dummy_map_filepath1 = "%s/etc/nagvis/maps/blabla.cfg" % site.root
    dummy_map_filepath2 = "%s/etc/nagvis/maps/bloblo.cfg" % site.root
    try:
        open(dummy_map_filepath1, "w")
        open(dummy_map_filepath2, "w")

        attributes = {"alias": "nagvis_test_alias", "nagvis_maps": ["blabla"]}

        if cmk.is_managed_edition():
            attributes["customer"] = "provider"

        web.add_group("contact", "nagvis_test", attributes)

        attributes["nagvis_maps"] = ["bloblo"]
        web.edit_group("contact", "nagvis_test", attributes)

        all_groups = web.get_all_groups("contact")
        assert "nagvis_test" in all_groups
        assert "bloblo" in all_groups["nagvis_test"]["nagvis_maps"]
    finally:
        web.delete_group("contact", "nagvis_test")
        os.unlink(dummy_map_filepath1)
        os.unlink(dummy_map_filepath2)
Exemple #15
0
    NEGATE,
    wato_root_dir,
    multisite_dir,
    rename_host_in_list,
    convert_cgroups_from_tuple,
    host_attribute_matches,
    default_site,
    format_config_value,
    liveproxyd_config_dir,
    mk_repr,
    mk_eval,
    has_agent_bakery,
    site_neutral_path,
)
from cmk.gui.watolib.wato_background_job import WatoBackgroundJob
if cmk.is_managed_edition():
    import cmk.gui.cme.managed as managed  # pylint: disable=no-name-in-module

from cmk.gui.plugins.watolib.utils import (
    ABCConfigDomain,
    config_domain_registry,
    config_variable_registry,
    wato_fileheader,
    SampleConfigGenerator,
    sample_config_generator_registry,
)

import cmk.gui.plugins.watolib

if not cmk.is_raw_edition():
    import cmk.gui.cee.plugins.watolib  # pylint: disable=no-name-in-module
Exemple #16
0
    def _show_user_list(self):
        visible_custom_attrs = [
            (name, attr) for name, attr in userdb.get_user_attributes() if attr.show_in_table()
        ]

        users = userdb.load_users()

        entries = users.items()

        html.begin_form("bulk_delete_form", method="POST")

        roles = userdb.load_roles()
        timeperiods = watolib.timeperiods.load_timeperiods()
        contact_groups = load_contact_group_information()

        with table_element("users", None, empty_text=_("No users are defined yet.")) as table:
            online_threshold = time.time() - config.user_online_maxage
            for uid, user in sorted(entries, key=lambda x: x[1].get("alias", x[0]).lower()):
                table.row()

                # Checkboxes
                table.cell(html.render_input("_toggle_group",
                                             type_="button",
                                             class_="checkgroup",
                                             onclick="cmk.selection.toggle_all_rows();",
                                             value='X'),
                           sortable=False,
                           css="checkbox")

                if uid != config.user.id:
                    html.checkbox("_c_user_%s" % base64.b64encode(uid.encode("utf-8")))

                user_connection_id = userdb.cleanup_connection_id(user.get('connector'))
                connection = userdb.get_connection(user_connection_id)

                # Buttons
                table.cell(_("Actions"), css="buttons")
                if connection:  # only show edit buttons when the connector is available and enabled
                    edit_url = watolib.folder_preserving_link([("mode", "edit_user"),
                                                               ("edit", uid)])
                    html.icon_button(edit_url, _("Properties"), "edit")

                    clone_url = watolib.folder_preserving_link([("mode", "edit_user"),
                                                                ("clone", uid)])
                    html.icon_button(clone_url, _("Create a copy of this user"), "clone")

                delete_url = make_action_link([("mode", "users"), ("_delete", uid)])
                html.icon_button(delete_url, _("Delete"), "delete")

                notifications_url = watolib.folder_preserving_link([("mode", "user_notifications"),
                                                                    ("user", uid)])
                if watolib.load_configuration_settings().get("enable_rulebased_notifications"):
                    html.icon_button(notifications_url, _("Custom notification table of this user"),
                                     "notifications")

                # ID
                table.cell(_("ID"), uid)

                # Online/Offline
                if config.save_user_access_times:
                    last_seen = user.get('last_seen', 0)
                    if last_seen >= online_threshold:
                        title = _('Online')
                        img_txt = 'online'
                    elif last_seen != 0:
                        title = _('Offline')
                        img_txt = 'offline'
                    elif last_seen == 0:
                        title = _('Never logged in')
                        img_txt = 'inactive'

                    title += ' (%s %s)' % (render.date(last_seen), render.time_of_day(last_seen))
                    table.cell(_("Act."))
                    html.icon(title, img_txt)

                    table.cell(_("Last seen"))
                    if last_seen != 0:
                        html.write_text("%s %s" %
                                        (render.date(last_seen), render.time_of_day(last_seen)))
                    else:
                        html.write_text(_("Never logged in"))

                if cmk.is_managed_edition():
                    table.cell(_("Customer"), managed.get_customer_name(user))

                # Connection
                if connection:
                    table.cell(_("Connection"),
                               '%s (%s)' % (connection.short_title(), user_connection_id))
                    locked_attributes = userdb.locked_attributes(user_connection_id)
                else:
                    table.cell(_("Connection"),
                               "%s (%s) (%s)" % (_("UNKNOWN"), user_connection_id, _("disabled")),
                               css="error")
                    locked_attributes = []

                # Authentication
                if "automation_secret" in user:
                    auth_method = _("Automation")
                elif user.get("password") or 'password' in locked_attributes:
                    auth_method = _("Password")
                else:
                    auth_method = "<i>%s</i>" % _("none")
                table.cell(_("Authentication"), auth_method)

                table.cell(_("State"))
                if user.get("locked", False):
                    html.icon(_('The login is currently locked'), 'user_locked')

                if "disable_notifications" in user and isinstance(user["disable_notifications"],
                                                                  bool):
                    disable_notifications_opts = {"disable": user["disable_notifications"]}
                else:
                    disable_notifications_opts = user.get("disable_notifications", {})

                if disable_notifications_opts.get("disable", False):
                    html.icon(_('Notifications are disabled'), 'notif_disabled')

                # Full name / Alias
                table.text_cell(_("Alias"), user.get("alias", ""))

                # Email
                table.text_cell(_("Email"), user.get("email", ""))

                # Roles
                table.cell(_("Roles"))
                if user.get("roles", []):
                    role_links = [(watolib.folder_preserving_link([("mode", "edit_role"),
                                                                   ("edit", role)]),
                                   roles[role].get("alias")) for role in user["roles"]]
                    html.write_html(
                        HTML(", ").join(
                            html.render_a(alias, href=link) for (link, alias) in role_links))

                # contact groups
                table.cell(_("Contact groups"))
                cgs = user.get("contactgroups", [])
                if cgs:
                    cg_aliases = [
                        contact_groups[c]['alias'] if c in contact_groups else c for c in cgs
                    ]
                    cg_urls = [
                        watolib.folder_preserving_link([("mode", "edit_contact_group"),
                                                        ("edit", c)]) for c in cgs
                    ]
                    html.write_html(
                        HTML(", ").join(
                            html.render_a(content, href=url)
                            for (content, url) in zip(cg_aliases, cg_urls)))
                else:
                    html.i(_("none"))

                #table.cell(_("Sites"))
                #html.write(vs_authorized_sites().value_to_text(user.get("authorized_sites",
                #                                                vs_authorized_sites().default_value())))

                # notifications
                if not watolib.load_configuration_settings().get("enable_rulebased_notifications"):
                    table.cell(_("Notifications"))
                    if not cgs:
                        html.i(_("not a contact"))
                    elif not user.get("notifications_enabled", True):
                        html.write_text(_("disabled"))
                    elif user.get("host_notification_options", "") == "" and \
                         user.get("service_notification_options", "") == "":
                        html.write_text(_("all events disabled"))
                    else:
                        tp = user.get("notification_period", "24X7")
                        if tp not in timeperiods:
                            tp = tp + _(" (invalid)")
                        elif tp not in watolib.timeperiods.builtin_timeperiods():
                            url = watolib.folder_preserving_link([("mode", "edit_timeperiod"),
                                                                  ("edit", tp)])
                            tp = html.render_a(timeperiods[tp].get("alias", tp), href=url)
                        else:
                            tp = timeperiods[tp].get("alias", tp)
                        html.write(tp)

                # the visible custom attributes
                for name, attr in visible_custom_attrs:
                    vs = attr.valuespec()
                    table.cell(html.attrencode(_u(vs.title())))
                    html.write(vs.value_to_text(user.get(name, vs.default_value())))

        html.button("_bulk_delete_users", _("Bulk Delete"), "submit", style="margin-top:10px")
        html.hidden_fields()
        html.end_form()

        if not load_contact_group_information():
            url = "wato.py?mode=contact_groups"
            html.open_div(class_="info")
            html.write(
                _("Note: you haven't defined any contact groups yet. If you <a href='%s'>"
                  "create some contact groups</a> you can assign users to them und thus "
                  "make them monitoring contacts. Only monitoring contacts can receive "
                  "notifications.") % url)
            html.write(" you can assign users to them und thus "
                       "make them monitoring contacts. Only monitoring contacts can receive "
                       "notifications.")
            html.close_div()
Exemple #17
0
    def action(self):
        if not html.check_transaction():
            return "users"

        if self._is_new_user:
            self._user_id = UserID(allow_empty=False).from_html_vars("user_id")
            user_attrs = {}
        else:
            self._user_id = html.get_unicode_input("edit").strip()
            user_attrs = self._users[self._user_id]

        # Full name
        user_attrs["alias"] = html.get_unicode_input("alias").strip()

        # Locking
        user_attrs["locked"] = html.get_checkbox("locked")
        increase_serial = False

        if self._user_id in self._users and self._users[
                self._user_id]["locked"] != user_attrs["locked"] and user_attrs["locked"]:
            increase_serial = True  # when user is being locked now, increase the auth serial

        # Authentication: Password or Secret
        auth_method = html.request.var("authmethod")
        if auth_method == "secret":
            secret = html.request.var("_auth_secret", "").strip()
            user_attrs["automation_secret"] = secret
            user_attrs["password"] = hash_password(secret)
            increase_serial = True  # password changed, reflect in auth serial

        else:
            password = html.request.var("_password_" + self._pw_suffix(), '').strip()
            password2 = html.request.var("_password2_" + self._pw_suffix(), '').strip()

            # We compare both passwords only, if the user has supplied
            # the repeation! We are so nice to our power users...
            # Note: this validation is done before the main-validiation later on
            # It doesn't make any sense to put this block into the main validation function
            if password2 and password != password2:
                raise MKUserError("_password2", _("The both passwords do not match."))

            # Detect switch back from automation to password
            if "automation_secret" in user_attrs:
                del user_attrs["automation_secret"]
                if "password" in user_attrs:
                    del user_attrs["password"]  # which was the encrypted automation password!

            if password:
                user_attrs["password"] = hash_password(password)
                user_attrs["last_pw_change"] = int(time.time())
                increase_serial = True  # password changed, reflect in auth serial

            # PW change enforcement
            user_attrs["enforce_pw_change"] = html.get_checkbox("enforce_pw_change")
            if user_attrs["enforce_pw_change"]:
                increase_serial = True  # invalidate all existing user sessions, enforce relogon

        # Increase serial (if needed)
        if increase_serial:
            user_attrs["serial"] = user_attrs.get("serial", 0) + 1

        # Email address
        user_attrs["email"] = EmailAddressUnicode().from_html_vars("email")

        idle_timeout = watolib.get_vs_user_idle_timeout().from_html_vars("idle_timeout")
        user_attrs["idle_timeout"] = idle_timeout
        if idle_timeout is not None:
            user_attrs["idle_timeout"] = idle_timeout
        elif idle_timeout is None and "idle_timeout" in user_attrs:
            del user_attrs["idle_timeout"]

        # Pager
        user_attrs["pager"] = html.request.var("pager", '').strip()

        if cmk.is_managed_edition():
            customer = self._vs_customer.from_html_vars("customer")
            self._vs_customer.validate_value(customer, "customer")

            if customer != managed.default_customer_id():
                user_attrs["customer"] = customer
            elif "customer" in user_attrs:
                del user_attrs["customer"]

        vs_sites = self._vs_sites()
        authorized_sites = vs_sites.from_html_vars("authorized_sites")
        vs_sites.validate_value(authorized_sites, "authorized_sites")

        if authorized_sites is not None:
            user_attrs["authorized_sites"] = authorized_sites
        elif "authorized_sites" in user_attrs:
            del user_attrs["authorized_sites"]

        # Roles
        user_attrs["roles"] = [
            role for role in self._roles.keys() if html.get_checkbox("role_" + role)
        ]

        # Language configuration
        set_lang = html.get_checkbox("_set_lang")
        language = html.request.var("language")
        if set_lang:
            if language == "":
                language = None
            user_attrs["language"] = language
        elif not set_lang and "language" in user_attrs:
            del user_attrs["language"]

        # Contact groups
        cgs = []
        for c in self._contact_groups:
            if html.get_checkbox("cg_" + c):
                cgs.append(c)
        user_attrs["contactgroups"] = cgs

        # Notification settings are only active if we do *not* have
        # rule based notifications!
        if not self._rbn_enabled():
            # Notifications
            user_attrs["notifications_enabled"] = html.get_checkbox("notifications_enabled")

            ntp = html.request.var("notification_period")
            if ntp not in self._timeperiods:
                ntp = "24X7"
            user_attrs["notification_period"] = ntp

            for what, opts in [("host", "durfs"), ("service", "wucrfs")]:
                user_attrs[what + "_notification_options"] = "".join(
                    [opt for opt in opts if html.get_checkbox(what + "_" + opt)])

            value = watolib.get_vs_flexible_notifications().from_html_vars("notification_method")
            user_attrs["notification_method"] = value
        else:
            user_attrs["fallback_contact"] = html.get_checkbox("fallback_contact")

        # Custom user attributes
        for name, attr in userdb.get_user_attributes():
            value = attr.valuespec().from_html_vars('ua_' + name)
            user_attrs[name] = value

        # Generate user "object" to update
        user_object = {self._user_id: {"attributes": user_attrs, "is_new_user": self._is_new_user}}
        # The following call validates and updated the users
        edit_users(user_object)
        return "users"
Exemple #18
0
    def page(self):
        # Let exceptions from loading notification scripts happen now
        watolib.load_notification_scripts()

        html.begin_form("user", method="POST")
        html.prevent_password_auto_completion()

        forms.header(_("Identity"))

        # ID
        forms.section(_("Username"), simple=not self._is_new_user)
        if self._is_new_user:
            vs_user_id = UserID(allow_empty=False)

        else:
            vs_user_id = FixedValue(self._user_id)
        vs_user_id.render_input("user_id", self._user_id)

        def lockable_input(name, dflt):
            if not self._is_locked(name):
                html.text_input(name, self._user.get(name, dflt), size=50)
            else:
                html.write_text(self._user.get(name, dflt))
                html.hidden_field(name, self._user.get(name, dflt))

        # Full name
        forms.section(_("Full name"))
        lockable_input('alias', self._user_id)
        html.help(_("Full name or alias of the user"))

        # Email address
        forms.section(_("Email address"))
        email = self._user.get("email", "")
        if not self._is_locked("email"):
            EmailAddressUnicode().render_input("email", email)
        else:
            html.write_text(email)
            html.hidden_field("email", email)

        html.help(
            _("The email address is optional and is needed "
              "if the user is a monitoring contact and receives notifications "
              "via Email."))

        forms.section(_("Pager address"))
        lockable_input('pager', '')
        html.help(_("The pager address is optional "))

        if cmk.is_managed_edition():
            forms.section(self._vs_customer.title())
            self._vs_customer.render_input("customer", managed.get_customer_id(self._user))

            html.help(self._vs_customer.help())

        vs_sites = self._vs_sites()
        forms.section(vs_sites.title())
        authorized_sites = self._user.get("authorized_sites", vs_sites.default_value())
        if not self._is_locked("authorized_sites"):
            vs_sites.render_input("authorized_sites", authorized_sites)
        else:
            html.write_html(vs_sites.value_to_text(authorized_sites))
        html.help(vs_sites.help())

        self._show_custom_user_attributes('ident')

        forms.header(_("Security"))
        forms.section(_("Authentication"))

        is_automation = self._user.get("automation_secret", None) is not None
        html.radiobutton("authmethod", "password", not is_automation,
                         _("Normal user login with password"))
        html.open_ul()
        html.open_table()
        html.open_tr()
        html.td(_("password:"******"_password_" + self._pw_suffix(), autocomplete="new-password")
            html.close_td()
            html.close_tr()

            html.open_tr()
            html.td(_("repeat:"))
            html.open_td()
            html.password_input("_password2_" + self._pw_suffix(), autocomplete="new-password")
            html.write_text(" (%s)" % _("optional"))
            html.close_td()
            html.close_tr()

            html.open_tr()
            html.td("%s:" % _("Enforce change"))
            html.open_td()
            # Only make password enforcement selection possible when user is allowed to change the PW
            if self._is_new_user or config.user_may(self._user_id,
                                                    'general.edit_profile') and config.user_may(
                                                        self._user_id, 'general.change_password'):
                html.checkbox("enforce_pw_change",
                              self._user.get("enforce_pw_change", False),
                              label=_("Change password at next login or access"))
            else:
                html.write_text(
                    _("Not permitted to change the password. Change can not be enforced."))
        else:
            html.i(_('The password can not be changed (It is locked by the user connector).'))
            html.hidden_field('_password', '')
            html.hidden_field('_password2', '')

        html.close_td()
        html.close_tr()
        html.close_table()
        html.close_ul()

        html.radiobutton("authmethod", "secret", is_automation,
                         _("Automation secret for machine accounts"))

        html.open_ul()
        html.text_input("_auth_secret",
                        self._user.get("automation_secret", ""),
                        size=30,
                        id_="automation_secret")
        html.write_text(" ")
        html.open_b(style=["position: relative", "top: 4px;"])
        html.write(" &nbsp;")
        html.icon_button("javascript:cmk.wato.randomize_secret('automation_secret', 20);",
                         _("Create random secret"), "random")
        html.close_b()
        html.close_ul()

        html.help(
            _("If you want the user to be able to login "
              "then specify a password here. Users without a login make sense "
              "if they are monitoring contacts that are just used for "
              "notifications. The repetition of the password is optional. "
              "<br>For accounts used by automation processes (such as fetching "
              "data from views for further procession), set the method to "
              "<u>secret</u>. The secret will be stored in a local file. Processes "
              "with read access to that file will be able to use Multisite as "
              "a webservice without any further configuration."))

        # Locking
        forms.section(_("Disable password"), simple=True)
        if not self._is_locked('locked'):
            html.checkbox("locked",
                          self._user.get("locked", False),
                          label=_("disable the login to this account"))
        else:
            html.write_text(
                _('Login disabled') if self._user.get("locked", False) else _('Login possible'))
            html.hidden_field('locked', '1' if self._user.get("locked", False) else '')
        html.help(
            _("Disabling the password will prevent a user from logging in while "
              "retaining the original password. Notifications are not affected "
              "by this setting."))

        forms.section(_("Idle timeout"))
        idle_timeout = self._user.get("idle_timeout")
        if not self._is_locked("idle_timeout"):
            watolib.get_vs_user_idle_timeout().render_input("idle_timeout", idle_timeout)
        else:
            html.write_text(idle_timeout)
            html.hidden_field("idle_timeout", idle_timeout)

        # Roles
        forms.section(_("Roles"))
        entries = self._roles.items()
        entries.sort(key=lambda x: (x[1]["alias"], x[0]))
        is_member_of_at_least_one = False
        for role_id, role in entries:
            if not self._is_locked("roles"):
                html.checkbox("role_" + role_id, role_id in self._user.get("roles", []))
                url = watolib.folder_preserving_link([("mode", "edit_role"), ("edit", role_id)])
                html.a(role["alias"], href=url)
                html.br()
            else:
                is_member = role_id in self._user.get("roles", [])
                if is_member:
                    is_member_of_at_least_one = True
                    url = watolib.folder_preserving_link([("mode", "edit_role"), ("edit", role_id)])
                    html.a(role["alias"], href=url)
                    html.br()

                html.hidden_field("role_" + role_id, '1' if is_member else '')
        if self._is_locked('roles') and not is_member_of_at_least_one:
            html.i(_('No roles assigned.'))
        self._show_custom_user_attributes('security')

        # Contact groups
        forms.header(_("Contact Groups"), isopen=False)
        forms.section()
        groups_page_url = watolib.folder_preserving_link([("mode", "contact_groups")])
        group_assign_url = watolib.folder_preserving_link([("mode", "rulesets"),
                                                           ("group", "grouping")])
        if not self._contact_groups:
            html.write(
                _("Please first create some <a href='%s'>contact groups</a>") % groups_page_url)
        else:
            entries = sorted([(group['alias'] or c, c) for c, group in self._contact_groups.items()
                             ])
            is_member_of_at_least_one = False
            for alias, gid in entries:
                is_member = gid in self._user.get("contactgroups", [])

                if not self._is_locked('contactgroups'):
                    html.checkbox("cg_" + gid, gid in self._user.get("contactgroups", []))
                else:
                    if is_member:
                        is_member_of_at_least_one = True
                    html.hidden_field("cg_" + gid, '1' if is_member else '')

                if not self._is_locked('contactgroups') or is_member:
                    url = watolib.folder_preserving_link([("mode", "edit_contact_group"),
                                                          ("edit", gid)])
                    html.a(alias, href=url)
                    html.br()

            if self._is_locked('contactgroups') and not is_member_of_at_least_one:
                html.i(_('No contact groups assigned.'))

        html.help(
            _("Contact groups are used to assign monitoring "
              "objects to users. If you haven't defined any contact groups yet, "
              "then first <a href='%s'>do so</a>. Hosts and services can be "
              "assigned to contact groups using <a href='%s'>rules</a>.<br><br>"
              "If you do not put the user into any contact group "
              "then no monitoring contact will be created for the user.") %
            (groups_page_url, group_assign_url))

        forms.header(_("Notifications"), isopen=False)
        if not self._rbn_enabled():
            forms.section(_("Enabling"), simple=True)
            html.checkbox("notifications_enabled",
                          self._user.get("notifications_enabled", False),
                          label=_("enable notifications"))
            html.help(
                _("Notifications are sent out "
                  "when the status of a host or service changes."))

            # Notification period
            forms.section(_("Notification time period"))
            choices = [(id_, "%s" % (tp["alias"])) for (id_, tp) in self._timeperiods.items()]
            html.dropdown("notification_period",
                          choices,
                          deflt=self._user.get("notification_period"),
                          ordered=True)
            html.help(
                _("Only during this time period the "
                  "user will get notifications about host or service alerts."))

            # Notification options
            notification_option_names = {  # defined here: _() must be executed always!
                "host": {
                    "d": _("Host goes down"),
                    "u": _("Host gets unreachble"),
                    "r": _("Host goes up again"),
                },
                "service": {
                    "w": _("Service goes into warning state"),
                    "u": _("Service goes into unknown state"),
                    "c": _("Service goes into critical state"),
                    "r": _("Service recovers to OK"),
                },
                "both": {
                    "f": _("Start or end of flapping state"),
                    "s": _("Start or end of a scheduled downtime"),
                }
            }

            forms.section(_("Notification Options"))
            for title, what, opts in [(_("Host events"), "host", "durfs"),
                                      (_("Service events"), "service", "wucrfs")]:
                html.write_text("%s:" % title)
                html.open_ul()

                user_opts = self._user.get(what + "_notification_options", opts)
                for opt in opts:
                    opt_name = notification_option_names[what].get(
                        opt, notification_option_names["both"].get(opt))
                    html.checkbox(what + "_" + opt, opt in user_opts, label=opt_name)
                    html.br()
                html.close_ul()

            html.help(
                _("Here you specify which types of alerts "
                  "will be notified to this contact. Note: these settings will only be saved "
                  "and used if the user is member of a contact group."))

            forms.section(_("Notification Method"))
            watolib.get_vs_flexible_notifications().render_input(
                "notification_method", self._user.get("notification_method"))

        else:
            forms.section(_("Fallback notifications"), simple=True)

            html.checkbox("fallback_contact",
                          self._user.get("fallback_contact", False),
                          label=_("Receive fallback notifications"))

            html.help(
                _("In case none of your notification rules handles a certain event a notification "
                  "will be sent to this contact. This makes sure that in that case at least <i>someone</i> "
                  "gets notified. Furthermore this contact will be used for notifications to any host or service "
                  "that is not known to the monitoring. This can happen when you forward notifications "
                  "from the Event Console.<br><br>Notification fallback can also configured in the global "
                  "setting <a href=\"wato.py?mode=edit_configvar&varname=notification_fallback_email\">"
                  "Fallback email address for notifications</a>."))

        self._show_custom_user_attributes('notify')

        forms.header(_("Personal Settings"), isopen=False)
        select_language(self._user)
        self._show_custom_user_attributes('personal')

        # Later we could add custom macros here, which then could be used
        # for notifications. On the other hand, if we implement some check_mk
        # --notify, we could directly access the data in the account with the need
        # to store values in the monitoring core. We'll see what future brings.
        forms.end()
        html.button("save", _("Save"))
        if self._is_new_user:
            html.set_focus("user_id")
        else:
            html.set_focus("alias")
        html.hidden_fields()
        html.end_form()