Example #1
0
 def valuespec(self):
     return EmailAddress(
         title=_("Fallback email address for notifications"),
         help=
         _("In case none of your notification rules handles a certain event a notification "
           "will be sent to this address. This makes sure that in that case at least <i>someone</i> "
           "gets notified. Furthermore this email address will be used in notifications as a "
           "contact for 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 single "
           "user profiles."),
         empty_text=_("(No fallback email address configured!)"),
         make_clickable=False,
     )
Example #2
0
def vs_crash_report():
    return Dictionary(
        title=_("Crash Report"),
        elements=[
            ("name", TextUnicode(
                title=_("Name"),
                allow_empty=False,
            )),
            ("mail", EmailAddress(
                title=_("Email Address"),
                allow_empty=False,
            )),
        ],
        optional_keys=[],
        render="form",
    )
Example #3
0
def _vs_add_common_mail_elements(elements):
    header = [
        ("from",
         Transform(
             Dictionary(
                 title="From",
                 elements=[
                     ("address",
                      EmailAddress(
                          title=_("Email address"),
                          size=40,
                          allow_empty=False,
                      )),
                     ("display_name",
                      TextUnicode(
                          title=_("Display name"),
                          size=40,
                          allow_empty=False,
                      )),
                 ],
                 help=
                 _("The email address and visible name used in the From header "
                   "of notifications messages. If no email address is specified "
                   "the default address is <tt>OMD_SITE@FQDN</tt> is used. If the "
                   "environment variable <tt>OMD_SITE</tt> is not set it defaults "
                   "to <tt>checkmk</tt>."),
             ),
             forth=lambda x: x if isinstance(x, dict) else {'address': x},
         )),
        ("reply_to",
         Transform(
             Dictionary(
                 title="Reply to",
                 elements=[
                     ("address",
                      EmailAddress(
                          title=_("Email address"),
                          size=40,
                          allow_empty=False,
                      )),
                     ("display_name",
                      TextUnicode(
                          title=_("Display name"),
                          size=40,
                          allow_empty=False,
                      )),
                 ],
                 required_keys=["address"],
                 help=_(
                     "The email address and visible name used in the Reply-To header "
                     "of notifications messages."),
             ),
             forth=lambda x: x if isinstance(x, dict) else {'address': x},
         )),
        ("host_subject",
         TextUnicode(
             title=_("Subject for host notifications"),
             help=_(
                 "Here you are allowed to use all macros that are defined in the "
                 "notification context."),
             default_value="Check_MK: $HOSTNAME$ - $EVENT_TXT$",
             size=64,
         )),
        ("service_subject",
         TextUnicode(
             title=_("Subject for service notifications"),
             help=_(
                 "Here you are allowed to use all macros that are defined in the "
                 "notification context."),
             default_value="Check_MK: $HOSTNAME$/$SERVICEDESC$ $EVENT_TXT$",
             size=64,
         )),
    ]

    footer = [
        ('bulk_sort_order',
         DropdownChoice(
             choices=[
                 ('oldest_first', _('Oldest first')),
                 ('newest_first', _('Newest first')),
             ],
             help=
             _("With this option you can specify, whether the oldest (default) or "
               "the newest notification should get shown at the top of the notification mail."
               ),
             title=_("Notification sort order for bulk notifications"),
             default_value="oldest_first",
         )),
        ("disable_multiplexing",
         FixedValue(
             True,
             title=_("Send seperate notifications to every recipient"),
             totext=_(
                 "A seperate notification is send to every recipient. Recipients "
                 "cannot see which other recipients were notified."),
             help=
             _("Per default only one notification is generated for all recipients. "
               "Therefore, all recipients can see who was notified and reply to "
               "all other recipients."),
         )),
    ]

    return header + elements + footer
Example #4
0
def _validate_user_attributes(all_users,
                              user_id,
                              user_attrs,
                              is_new_user=True):
    # Check user_id
    if is_new_user:
        if user_id in all_users:
            raise MKUserError(
                "user_id",
                _("This username is already being used by another user."))
        vs_user_id = UserID(allow_empty=False)
        vs_user_id.validate_value(user_id, "user_id")
    else:
        if user_id not in all_users:
            raise MKUserError(
                None, _("The user you are trying to edit does not exist."))

    # Full name
    alias = user_attrs.get("alias")
    if not alias:
        raise MKUserError(
            "alias",
            _("Please specify a full name or descriptive alias for the user."))

    # Locking
    locked = user_attrs.get("locked")
    if user_id == config.user.id and locked:
        raise MKUserError("locked", _("You cannot lock your own account!"))

    # Authentication: Password or Secret
    if "automation_secret" in user_attrs:
        secret = user_attrs["automation_secret"]
        if len(secret) < 10:
            raise MKUserError(
                'secret',
                _("Please specify a secret of at least 10 characters length."))
    else:
        password = user_attrs.get("password")
        if password:
            verify_password_policy(password)

    # Email
    email = user_attrs.get("email")
    vs_email = EmailAddress()
    vs_email.validate_value(email, "email")

    # Idle timeout
    idle_timeout = user_attrs.get("idle_timeout")
    vs_user_idle_timeout = get_vs_user_idle_timeout()
    vs_user_idle_timeout.validate_value(idle_timeout, "idle_timeout")

    # Notification settings are only active if we do *not* have rule based notifications!
    if not global_settings.rulebased_notifications_enabled():
        # Notifications
        notifications_enabled = user_attrs.get("notification_enabled")

        # Check if user can receive notifications
        if notifications_enabled:
            if not email:
                raise MKUserError(
                    "email",
                    _('You have enabled the notifications but missed to configure a '
                      'Email address. You need to configure your mail address in order '
                      'to be able to receive emails.'))

            contactgroups = user_attrs.get("contactgroups")
            if not contactgroups:
                raise MKUserError(
                    "notifications_enabled",
                    _('You have enabled the notifications but missed to make the '
                      'user member of at least one contact group. You need to make '
                      'the user member of a contact group which has hosts assigned '
                      'in order to be able to receive emails.'))

            roles = user_attrs.get("roles")
            if not roles:
                raise MKUserError(
                    "role_user",
                    _("Your user has no roles. Please assign at least one role."
                      ))

        notification_method = user_attrs.get("notification_method")
        get_vs_flexible_notifications().validate_value(notification_method,
                                                       "notification_method")
    else:
        fallback_contact = user_attrs.get("fallback_contact")
        if fallback_contact and not email:
            raise MKUserError(
                "email",
                _("You have enabled the fallback notifications but missed to configure an "
                  "email address. You need to configure your mail address in order "
                  "to be able to receive fallback notifications."))

    # Custom user attributes
    for name, attr in userdb.get_user_attributes():
        value = user_attrs.get(name)
        attr.valuespec().validate_value(value, "ua_" + name)
def _valuespec_active_checks_mail_loop():
    return Transform(
        Dictionary(
            title=_("Check Email Delivery"),
            help=
            _("This active check sends out special E-Mails to a defined mail address using "
              "the SMTP protocol and then tries to receive these mails back by querying the "
              "inbox of a IMAP or POP3 mailbox. With this check you can verify that your whole "
              "mail delivery progress is working."),
            optional_keys=[
                "subject",
                "smtp_server",
                "smtp_tls",
                "smtp_port",
                "smtp_auth",
                "connect_timeout",
                "delete_messages",
                "duration",
            ],
            elements=[
                (
                    "item",
                    TextInput(
                        title=_("Name"),
                        help=
                        _("The service description will be <b>Mail Loop</b> plus this name"
                          ),
                        allow_empty=False,
                    ),
                ),
                (
                    "subject",
                    TextInput(
                        title=_("Subject"),
                        allow_empty=False,
                        help=_(
                            "Here you can specify the subject text "
                            "instead of default text 'Check_MK-Mail-Loop'."),
                    ),
                ),
                (
                    "smtp_server",
                    TextInput(
                        title=_("SMTP Server"),
                        allow_empty=False,
                        help=_(
                            "You can specify a hostname or IP address different from the IP address "
                            "of the host this check will be assigned to."),
                    ),
                ),
                (
                    "smtp_tls",
                    FixedValue(
                        value=True,
                        title=_("Use TLS over SMTP"),
                        totext=_("Encrypt SMTP communication using TLS"),
                    ),
                ),
                (
                    "smtp_port",
                    Integer(
                        title=_("SMTP TCP Port to connect to"),
                        help=
                        _("The TCP Port the SMTP server is listening on. Defaulting to <tt>25</tt>."
                          ),
                        default_value=25,
                    ),
                ),
                (
                    "smtp_auth",
                    Tuple(
                        title=_("SMTP Authentication"),
                        elements=[
                            TextInput(title=_("Username"),
                                      allow_empty=False,
                                      size=24),
                            IndividualOrStoredPassword(title=_("Password"),
                                                       allow_empty=False,
                                                       size=12),
                        ],
                    ),
                ),
                _mail_receiving_params({"IMAP", "POP3"}),
                (
                    "mail_from",
                    EmailAddress(title=_("From: email address"), ),
                ),
                (
                    "mail_to",
                    EmailAddress(title=_("Destination email address"), ),
                ),
                (
                    "connect_timeout",
                    Integer(
                        title=_("Connect Timeout"),
                        minvalue=1,
                        default_value=10,
                        unit=_("sec"),
                    ),
                ),
                (
                    "duration",
                    Tuple(
                        title=_("Loop duration"),
                        elements=[
                            Age(title=_("Warning at")),
                            Age(title=_("Critical at")),
                        ],
                    ),
                ),
                (
                    "delete_messages",
                    FixedValue(
                        value=True,
                        title=_("Delete processed messages"),
                        totext=
                        _("Delete all processed message belonging to this check"
                          ),
                        help=
                        _("Delete all messages identified as being related to this "
                          "check. This is disabled by default, which will make "
                          "your mailbox grow when you do not clean it up on your own."
                          ),
                    ),
                ),
            ],
        ),
        forth=transform_check_mail_loop_params,
    )
Example #6
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"):
            EmailAddress().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_version.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
            uid = None if self._user_id is None else UserId(self._user_id)
            if (self._is_new_user
                    or (config.user_may(uid, 'general.edit_profile')
                        and config.user_may(uid, '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"))
        is_member_of_at_least_one = False
        for role_id, role in sorted(self._roles.items(),
                                    key=lambda x: (x[1]["alias"], x[0])):
            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"))
            user_np = self._user.get("notification_period")
            if not isinstance(user_np, str):
                raise Exception("invalid notification period %r" % (user_np, ))
            html.dropdown("notification_period",
                          [(id_, "%s" % (tp["alias"]))
                           for (id_, tp) in self._timeperiods.items()],
                          deflt=user_np,
                          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()
Example #7
0
    def action(self):
        if not html.check_transaction():
            return "users"

        if self._user_id is None:  # same as self._is_new_user
            self._user_id = UserID(allow_empty=False).from_html_vars("user_id")
            user_attrs = {}
        else:
            self._user_id = html.request.get_unicode_input_mandatory(
                "edit").strip()
            user_attrs = self._users[UserId(self._user_id)]

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

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

        if (UserId(self._user_id) in self._users and self._users[UserId(
                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.get_str_input_mandatory("_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.get_str_input_mandatory(
                "_password_" + self._pw_suffix(), '').strip()
            password2 = html.request.get_str_input_mandatory(
                "_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"] = EmailAddress().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.get_str_input_mandatory(
            "pager", '').strip()

        if cmk_version.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"
    def spec(self):
        return Dictionary(
            title=_("Create notification with the following parameters"),
            elements=[
                ("from",
                 EmailAddress(
                     title=_("From: Address"),
                     size=40,
                     allow_empty=False,
                 )),
                ("host_subject",
                 TextUnicode(
                     title=_("Subject for host notifications"),
                     help=_(
                         "Here you are allowed to use all macros that are defined in the "
                         "notification context."),
                     default_value="Check_MK: $HOSTNAME$ - $EVENT_TXT$",
                     size=64,
                 )),
                ("service_subject",
                 TextUnicode(
                     title=_("Subject for service notifications"),
                     help=_(
                         "Here you are allowed to use all macros that are defined in the "
                         "notification context."),
                     default_value=
                     "Check_MK: $HOSTNAME$/$SERVICEDESC$ $EVENT_TXT$",
                     size=64,
                 )),
                ("common_body",
                 TextAreaUnicode(
                     title=_(
                         "Body head for both host and service notifications"),
                     rows=7,
                     cols=58,
                     monospaced=True,
                     default_value="""Host:     $HOSTNAME$
Alias:    $HOSTALIAS$
Address:  $HOSTADDRESS$
""",
                 )),
                ("host_body",
                 TextAreaUnicode(
                     title=_("Body tail for host notifications"),
                     rows=9,
                     cols=58,
                     monospaced=True,
                     default_value="""Event:    $EVENT_TXT$
Output:   $HOSTOUTPUT$
Perfdata: $HOSTPERFDATA$
$LONGHOSTOUTPUT$
""",
                 )),
                ("service_body",
                 TextAreaUnicode(
                     title=_("Body tail for service notifications"),
                     rows=11,
                     cols=58,
                     monospaced=True,
                     default_value="""Service:  $SERVICEDESC$
Event:    $EVENT_TXT$
Output:   $SERVICEOUTPUT$
Perfdata: $SERVICEPERFDATA$
$LONGSERVICEOUTPUT$
""",
                 )),
            ],
        )
def _common_email_parameters(protocol, port_defaults):
    return Dictionary(
        title=protocol,
        optional_keys=["server", "email_address"],
        elements=[
            (
                "server",
                HostAddress(
                    title=f"{protocol} Server",
                    allow_empty=False,
                    help=_(
                        "You can specify a hostname or IP address different from the IP address "
                        "of the host this check will be assigned to."),
                ),
            ),
            (
                "connection",
                Dictionary(
                    required_keys=[],
                    title=_("Connection settings"),
                    elements=[
                        (
                            "disable_tls",
                            Checkbox(
                                title=_("Disable TLS/SSL"),
                                label=_("Force unencrypted communication"),
                            ),
                        ),
                        (
                            "disable_cert_validation",
                            Checkbox(
                                title=_("Disable certificate validation"),
                                label=
                                _("Ignore unsuccessful validation (in case of TLS/SSL)"
                                  ),
                            ),
                        ),
                        (
                            "tcp_port",
                            Integer(
                                title=_("TCP Port"),
                                label=_("(default is %r for %s/TLS)") %
                                (port_defaults, protocol),
                            ),
                        ),
                    ],
                ),
            ),
            (
                "auth",
                Tuple(
                    title=_("Authentication"),
                    elements=[
                        TextInput(title=_("Username"), allow_empty=False),
                        IndividualOrStoredPassword(
                            title=_("Password"), allow_empty=False, size=12),
                    ],
                ),
            ),
        ] + ([(
            "email_address",
            EmailAddress(
                title=_("Email address used for account identification"),
                label=_("(overrides <b>username</b>)"),
                help=
                _("Used to specify the account to be contacted"
                  " (aka. 'PrimarySmtpAddress') in case it's different from the"
                  " username. If not specified the credentials username is used."
                  ),
                allow_empty=False,
            ),
        )] if protocol == "EWS" else []),  # type: ignore[arg-type]
    )