Beispiel #1
0
    def _from_vars(self):
        # TODO: Should we turn the both fields below into Optional[UserId]?
        self._user_id = html.request.get_unicode_input(
            "edit")  # missing -> new user
        # This is needed for the breadcrumb computation:
        # When linking from user notification rules page the request variable is "user"
        # instead of "edit". We should also change that variable to "user" on this page,
        # then we can simply use self._user_id.
        if not self._user_id and html.request.has_var("user"):
            self._user_id = html.request.get_str_input_mandatory("user")

        self._cloneid = html.request.get_unicode_input(
            "clone")  # Only needed in 'new' mode
        # TODO: Nuke the field below? It effectively hides facts about _user_id for mypy.
        self._is_new_user = self._user_id is None
        self._users = userdb.load_users(lock=html.is_transaction())
        new_user = userdb.new_user_template('htpasswd')
        if self._user_id is not None:
            self._user = self._users.get(UserId(self._user_id), new_user)
        elif self._cloneid:
            self._user = self._users.get(UserId(self._cloneid), new_user)
        else:
            self._user = new_user
        self._locked_attributes = userdb.locked_attributes(
            self._user.get('connector'))
Beispiel #2
0
    def _show_form(self, profile_changed: bool) -> None:
        assert config.user.id is not None

        users = userdb.load_users()

        change_reason = html.request.get_ascii_input('reason')

        if change_reason == 'expired':
            html.p(
                _('Your password is too old, you need to choose a new password.'
                  ))
        elif change_reason == 'enforced':
            html.p(
                _('You are required to change your password before proceeding.'
                  ))

        if profile_changed:
            html.show_message(_("Your password has been changed."))
            if change_reason:
                raise HTTPRedirect(
                    html.request.get_str_input_mandatory(
                        '_origtarget', 'index.py'))

        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

        locked_attributes = userdb.locked_attributes(user.get('connector'))
        if "password" in locked_attributes:
            raise MKUserError(
                "cur_password",
                _("You can not change your password, because it is "
                  "managed by another system."))

        html.begin_form("profile", method="POST")
        html.prevent_password_auto_completion()
        html.open_div(class_="wato")
        forms.header(self._page_title())

        forms.section(_("Current Password"))
        html.password_input('cur_password', autocomplete="new-password")

        forms.section(_("New Password"))
        html.password_input('password', autocomplete="new-password")

        forms.section(_("New Password Confirmation"))
        html.password_input('password2', autocomplete="new-password")

        forms.end()
        html.close_div()
        html.hidden_fields()
        html.end_form()
        html.footer()
Beispiel #3
0
    def _edit_users(self, request):
        # A dictionary with the userid as key
        # Each value is a {"set_attributes": {"alias": "test"},
        #                  "unset_attributes": ["pager", "email"]}
        user_settings = request.get("users")
        all_users = userdb.load_users()

        locked_attributes_by_connection = {}

        edit_user_objects = {}
        for user_id, settings in user_settings.items():
            if user_id not in all_users:
                raise MKUserError(None, _("Unknown user: %s") % user_id)

            user = all_users[user_id]
            connector_id = user.get("connector")

            set_attributes = settings.get("set_attributes", {})
            unset_attributes = settings.get("unset_attributes", [])

            if connector_id not in locked_attributes_by_connection:
                locked_attributes_by_connection[connector_id] = userdb.locked_attributes(
                    connector_id
                )

            locked_attributes = locked_attributes_by_connection[connector_id]
            for attr in list(set_attributes.keys()) + unset_attributes:
                if attr in locked_attributes:
                    raise MKUserError(
                        None,
                        _(
                            'Attribute "%s" of user "%s" can not be changed, '
                            "because it is locked by the user connection."
                        )
                        % (attr, user_id),
                    )

            user_attrs = copy.deepcopy(user)
            user_attrs.update(set_attributes)
            for entry in unset_attributes:
                if entry not in user_attrs:
                    continue
                # TODO: Dynamically fiddling around with a TypedDict is a bit questionable
                del user_attrs[entry]  # type: ignore[misc]

            new_password = set_attributes.get("password")
            if new_password:
                user_attrs["password"] = hash_password(new_password)
                user_attrs["serial"] = user_attrs.get("serial", 0) + 1

            edit_user_objects[user_id] = {"attributes": user_attrs, "is_new_user": False}

        cmk.gui.watolib.users.edit_users(edit_user_objects)
Beispiel #4
0
    def _show_form(self) -> None:
        assert user.id is not None

        users = userdb.load_users()

        change_reason = request.get_ascii_input("reason")

        if change_reason == "expired":
            html.p(
                _("Your password is too old, you need to choose a new password."
                  ))
        elif change_reason == "enforced":
            html.p(
                _("You are required to change your password before proceeding."
                  ))

        user_spec = users.get(user.id)
        if user_spec is None:
            html.show_warning(_("Sorry, your user account does not exist."))
            html.footer()
            return

        locked_attributes = userdb.locked_attributes(
            user_spec.get("connector"))
        if "password" in locked_attributes:
            raise MKUserError(
                "cur_password",
                _("You can not change your password, because it is "
                  "managed by another system."),
            )

        html.begin_form("profile", method="POST")
        html.prevent_password_auto_completion()
        html.open_div(class_="wato")
        forms.header(self._page_title())

        forms.section(_("Current Password"))
        html.password_input("cur_password", autocomplete="new-password")

        forms.section(_("New Password"))
        html.password_input("password", autocomplete="new-password")

        forms.section(_("New Password Confirmation"))
        html.password_input("password2", autocomplete="new-password")

        forms.end()
        html.close_div()
        html.hidden_fields()
        html.end_form()
        html.footer()
Beispiel #5
0
 def _from_vars(self):
     # TODO: Should we turn the both fields below into Optional[UserId]?
     self._user_id = html.request.get_unicode_input("edit")  # missing -> new user
     self._cloneid = html.request.get_unicode_input("clone")  # Only needed in 'new' mode
     # TODO: Nuke the field below? It effectively hides facts about _user_id for mypy.
     self._is_new_user = self._user_id is None
     self._users = userdb.load_users(lock=html.is_transaction())
     new_user = userdb.new_user_template('htpasswd')
     if self._user_id is not None:
         self._user = self._users.get(UserId(self._user_id), new_user)
     elif self._cloneid:
         self._user = self._users.get(UserId(self._cloneid), new_user)
     else:
         self._user = new_user
     self._locked_attributes = userdb.locked_attributes(self._user.get('connector'))
Beispiel #6
0
    def _from_vars(self):
        self._user_id = html.get_unicode_input("edit")  # missing -> new user
        self._cloneid = html.get_unicode_input("clone")  # Only needed in 'new' mode
        self._is_new_user = self._user_id is None

        self._users = userdb.load_users(lock=html.is_transaction())

        if self._is_new_user:
            if self._cloneid:
                self._user = self._users.get(self._cloneid, userdb.new_user_template('htpasswd'))
            else:
                self._user = userdb.new_user_template('htpasswd')
        else:
            self._user = self._users.get(self._user_id, userdb.new_user_template('htpasswd'))

        self._locked_attributes = userdb.locked_attributes(self._user.get('connector'))
Beispiel #7
0
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()
Beispiel #8
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()