Exemple #1
0
    def get_form(self):
        system_pw_form = fapi.ForisForm("system_password", self.data)
        system_pw_main = system_pw_form.add_section(
            name="set_password",
            title=_(self.userfriendly_title),
            description=_("In order to access the advanced configuration possibilities which are "
                          "not present here, you must set the root user's password. The advanced "
                          "configuration options can be managed either through the "
                          "<a href=\"//%(host)s/%(path)s\">LuCI web interface</a> "
                          "or over SSH.") % {'host': bottle.request.get_header('host'),
                                             'path': 'cgi-bin/luci'}
        )
        system_pw_main.add_field(Password, name="password", label=_("Password"), required=True,
                                 validators=validators.LenRange(6, 128))
        system_pw_main.add_field(Password, name="password_validation", label=_("Password (repeat)"),
                                 required=True,
                                 validators=validators.EqualTo("password", "password_validation",
                                                               _("Passwords are not equal.")))

        def system_pw_form_cb(data):
            client.set_password("root", data["password"])
            return "none", None

        system_pw_form.add_callback(system_pw_form_cb)
        return system_pw_form
Exemple #2
0
 def save(self, *args, **kwargs):
     result = False
     try:
         result = super(MaintenanceConfigPage, self).save(no_messages=True, *args, **kwargs)
         new_ip = self.form.callback_results.get('new_ip')
         if new_ip:
             # rebuild current URL with new IP
             old_urlparts = bottle.request.urlparts
             new_url = urlunsplit((old_urlparts.scheme, new_ip, old_urlparts.path, "", ""))
             messages.success(_("Configuration was successfully restored. After installing "
                                "updates and rebooting you can return to this page at "
                                "<a href=\"%(new_url)s\">%(new_url)s</a> in local "
                                "network. Please wait a while until router automatically "
                                "restarts.") % dict(new_url=new_url))
         elif result:
             messages.success(_("Configuration was successfully restored. Please wait a while "
                                "for installation of updates and automatic restart of the "
                                "device."))
             messages.warning(_("IP address of the router could not be determined from the backup."))
         else:
             messages.warning(_("There were some errors in your input."))
     except ConfigRestoreError:
         messages.error(_("Configuration could not be loaded, backup file is probably corrupted."))
         logger.exception("Error when restoring backup.")
     return result
Exemple #3
0
    def get_form(self):
        system_pw_form = fapi.ForisForm("system_password", self.data)
        system_pw_main = system_pw_form.add_section(
            name="set_password",
            title=_(self.userfriendly_title),
            description=
            _("In order to access the advanced configuration possibilities which are "
              "not present here, you must set the root user's password. The advanced "
              "configuration options can be managed either through the "
              "<a href=\"//%(host)s/%(path)s\">LuCI web interface</a> "
              "or over SSH.") % {
                  'host': bottle.request.get_header('host'),
                  'path': 'cgi-bin/luci'
              })
        system_pw_main.add_field(Password,
                                 name="password",
                                 label=_("Password"),
                                 required=True,
                                 validators=validators.LenRange(6, 128))
        system_pw_main.add_field(Password,
                                 name="password_validation",
                                 label=_("Password (repeat)"),
                                 required=True,
                                 validators=validators.EqualTo(
                                     "password", "password_validation",
                                     _("Passwords are not equal.")))

        def system_pw_form_cb(data):
            client.set_password("root", data["password"])
            return "none", None

        system_pw_form.add_callback(system_pw_form_cb)
        return system_pw_form
Exemple #4
0
    def get_form(self):
        form = ForisForm(
            "tor_plugin_check",
            self.data,
            filter=None  #create_config_filter("tor_plugin")
        )
        section = form.add_section(name="parameters",
                                   title=_("Tor configuration"))
        section.add_field(
            Checkbox,
            name="default_tor_route",
            label=_("All traffic through tor"),
            hint=_("After enabling this option all traffic from your client "
                   "will be routed through the tor network."),
        )

        def update_uci_callback(data):
            enable_route = data["default_tor_route"]
            print form.nuci_config().find_child(
                "uci.tor.omnia_specific.enable_routing").value
            if enable_route:
                print "Selected"
            else:
                print "Unselected"
            syslog.syslog("update_uci_callback")
            return "none", None

        def update_program_callback(data):
            syslog.syslog("foris tor plugin update_program_callback")
            return "none", None

        form.add_callback(update_uci_callback)
        #form.add_callback(update_program_callback)
        return form
Exemple #5
0
    def get_form(self):
        # form definitions
        pw_form = fapi.ForisForm("password", self.data)
        pw_main = pw_form.add_section(name="set_password", title=_(self.userfriendly_title),
                                      description=_("Set your password for this administration "
                                                    "interface. The password must be at least 6 "
                                                    "characters long."))
        if self.change:
            pw_main.add_field(Password, name="old_password", label=_("Current password"))
            label_pass1 = _("New password")
            label_pass2 = _("New password (repeat)")
        else:
            label_pass1 = _("Password")
            label_pass2 = _("Password (repeat)")

        pw_main.add_field(Password, name="password", label=label_pass1, required=True,
                          validators=validators.LenRange(6, 128))
        pw_main.add_field(Password, name="password_validation", label=label_pass2,
                          required=True,
                          validators=validators.EqualTo("password", "password_validation",
                                                        _("Passwords are not equal.")))
        pw_main.add_field(Checkbox, name="set_system_pw",
                          label=_("Use the same password for advanced configuration"),
                          hint=_("Same password would be used for accessing this administration "
                                 "interface, for root user in LuCI web interface and for SSH "
                                 "login. Use a strong password! (If you choose not to set the "
                                 "password for advanced configuration here, you will have the "
                                 "option to do so later. Until then, the root account will be "
                                 "blocked.)"))

        def pw_form_cb(data):
            from beaker.crypto import pbkdf2
            if self.change:
                # if changing password, check the old pw is right first
                uci_data = client.get(filter=filters.foris_config)
                password_hash = uci_data.find_child("uci.foris.auth.password")
                # allow changing the password if password_hash is empty
                if password_hash:
                    password_hash = password_hash.value
                    # crypt automatically extracts salt and iterations from formatted pw hash
                    if password_hash != pbkdf2.crypt(data['old_password'], salt=password_hash):
                        return "save_result", {'wrong_old_password': True}

            uci = Uci()
            foris = Config("foris")
            uci.add(foris)
            auth = Section("auth", "config")
            foris.add(auth)
            # use 48bit pseudo-random salt internally generated by pbkdf2
            new_password_hash = pbkdf2.crypt(data['password'], iterations=1000)
            auth.add(Option("password", new_password_hash))

            if data['set_system_pw'] is True:
                client.set_password("root", data['password'])

            return "edit_config", uci

        pw_form.add_callback(pw_form_cb)
        return pw_form
Exemple #6
0
 def save(self, *args, **kwargs):
     result = super(UpdaterConfigPage, self).save(no_messages=True, *args, **kwargs)
     if result:
         messages.success(_("Configuration was successfully saved. Selected "
                            "packages should be installed or removed shortly."))
     else:
         messages.warning(_("There were some errors in your input."))
     return result
Exemple #7
0
 def save(self, *args, **kwargs):
     no_messages = kwargs.pop("no_messages", False)
     result = super(ConfigPageMixin, self).save(*args, **kwargs)
     if no_messages:
         return result
     if result:
         messages.success(_("Configuration was successfully saved."))
     else:
         messages.warning(_("There were some errors in your input."))
     return result
Exemple #8
0
 def save(self, *args, **kwargs):
     result = super(PasswordConfigPage, self).save(no_messages=True, *args, **kwargs)
     wrong_old_password = self.form.callback_results.get('wrong_old_password', False)
     if wrong_old_password:
         messages.warning(_("Old password you entered was not valid."))
     elif result:
         messages.success(_("Password was successfully saved."))
     else:
         messages.warning(_("There were some errors in your input."))
     return result
Exemple #9
0
 def _action_save_notifications(self):
     if bottle.request.method != 'POST':
         messages.error("Wrong HTTP method.")
         bottle.redirect(reverse("config_page", page_name="maintenance"))
     handler = NotificationsHandler(request.POST)
     if handler.save():
         messages.success(_("Configuration was successfully saved."))
         bottle.redirect(reverse("config_page", page_name="maintenance"))
     messages.warning(_("There were some errors in your input."))
     return super(MaintenanceConfigPage, self).render(notifications_form=handler.form,
                                                      config_pages=config_page_map)
Exemple #10
0
    def get_form(self):
        maintenance_form = fapi.ForisForm("maintenance", self.data)
        maintenance_main = maintenance_form.add_section(name="restore_backup",
                                                        title=_(self.userfriendly_title))
        maintenance_main.add_field(File, name="backup_file", label=_("Backup file"), required=True)

        def maintenance_form_cb(data):
            result = client.load_config_backup(data['backup_file'].file)
            return "save_result", {'new_ip': result}

        maintenance_form.add_callback(maintenance_form_cb)
        return maintenance_form
Exemple #11
0
 def _action_test_notifications(self):
     if bottle.request.method != 'POST':
         messages.error("Wrong HTTP method.")
         bottle.redirect(reverse("config_page", page_name="maintenance"))
     result, error_message = client.test_notifications()
     if result:
         messages.success(_("Testing message was sent, please check your inbox."))
     else:
         if error_message:
             messages.error(_("Sending of the testing message failed, your configuration is possibly "
                              "wrong.<br>Error returned:<br><pre>%(error)s</pre>")
                            % dict(error=error_message))
         else:
             messages.error(_("Sending of the testing message failed because of an internal error."))
     bottle.redirect(reverse("config_page", page_name="maintenance"))
Exemple #12
0
    def get_form(self):
        maintenance_form = fapi.ForisForm("maintenance", self.data)
        maintenance_main = maintenance_form.add_section(
            name="restore_backup", title=_(self.userfriendly_title))
        maintenance_main.add_field(File,
                                   name="backup_file",
                                   label=_("Backup file"),
                                   required=True)

        def maintenance_form_cb(data):
            result = client.load_config_backup(data['backup_file'].file)
            return "save_result", {'new_ip': result}

        maintenance_form.add_callback(maintenance_form_cb)
        return maintenance_form
Exemple #13
0
 def __init__(self, low, high):
     self._low = low
     self._high = high
     super(InRange, self).__init__(
         _("Not in a valid range %(low)s - %(high)s.") %
         dict(low=low, high=high))
     self.js_validator_params = "[%s,%s]" % (low, high)
Exemple #14
0
 def __init__(self, low, high):
     self._low = low
     self._high = high
     super(ByteLenRange, self).__init__(
         _("Length must be from %(low)s to %(high)s characters.") %
         dict(low=low, high=high))
     self.js_validator_params = "[%s,%s]" % (low, high)
Exemple #15
0
 def __init__(self):
     pattern = r"^([01][0-9]|2[0-3]):([0-5][0-9])$"
     super(Time,
           self).__init__(_("This is not valid time in HH:MM format."),
                          pattern)
     self.js_validator = ("pattern", pattern)
     self.extra_data['parsley-error-message'] = self.msg
Exemple #16
0
def step_post(number=1):
    Wizard = get_wizard(number)
    wiz = Wizard(request.POST)
    if request.is_xhr:
        # only update is allowed via XHR
        return wiz.render(is_xhr=True)

    try:
        if wiz.save():
            bottle.redirect(reverse("wizard_step", number=int(number) + 1))
    except TypeError:
        # raised by Validator - could happen when the form is posted with wrong fields
        messages.error(_("Configuration could not be saved due to an internal error."))
        logger.exception("Error when saving form.")
    messages.warning(_("There were some errors in your input."))
    logger.warning("Form not saved.")
    return wiz.render(stepnumber=number)
Exemple #17
0
 def translate_sending_status(status):
     verbose = _(AboutConfigPage.SENDING_STATUS_TRANSLATION.get(
         status,
         AboutConfigPage.SENDING_STATUS_TRANSLATION['unknown']
     ))
     if status not in AboutConfigPage.SENDING_STATUS_TRANSLATION:
         verbose += " (%s)" % status
     return verbose
Exemple #18
0
    def get_form(self):
        form = fapi.ForisForm("registration_check",
                              self.data,
                              filter=create_config_filter("foris"))
        main_section = form.add_section(name="check_email",
                                        title=_(self.userfriendly_title))
        main_section.add_field(Email, name="email", label=_("Email"))

        def form_cb(data):
            result = client.get_registration_status(data.get("email"),
                                                    bottle.request.app.lang)
            return "save_result", {
                'success': result[0],
                'response': result[1],
            }

        form.add_callback(form_cb)
        return form
Exemple #19
0
    def get_form(self):
        dns_form = fapi.ForisForm("dns", self.data,
                                  filter=create_config_filter("unbound"))
        dns_main = dns_form.add_section(name="set_dns",
                                        title=_(self.userfriendly_title))
        dns_main.add_field(Checkbox, name="forward_upstream", label=_("Use forwarding"),
                           nuci_path="uci.unbound.server.forward_upstream",
                           nuci_preproc=lambda val: bool(int(val.value)), default=True)

        def dns_form_cb(data):
            uci = Uci()
            unbound = Config("unbound")
            uci.add(unbound)
            server = Section("server", "unbound")
            unbound.add(server)
            server.add(Option("forward_upstream", data['forward_upstream']))
            return "edit_config", uci

        dns_form.add_callback(dns_form_cb)
        return dns_form
Exemple #20
0
    def get_form(self):
        time_form = fapi.ForisForm("time", self.data, filter=filters.time)
        time_main = time_form.add_section(
            name="set_time",
            title=_(self.userfriendly_title),
            description=_("We could not synchronize the time with a timeserver, probably due to a "
                          "loss of connection. It is necessary for the router to have correct time "
                          "in order to function properly. Please, synchronize it with your "
                          "computer's time, or set it manually.")
        )

        time_main.add_field(Textbox, name="time", label=_("Time"), nuci_path="time",
                            nuci_preproc=lambda v: v.local)

        def time_form_cb(data):
            client.set_time(data['time'])
            return "none", None

        time_form.add_callback(time_form_cb)

        return time_form
Exemple #21
0
 def _get_channels(wifi_card):
     channels_2g4 = [("auto", _("auto"))]
     channels_5g = []
     for channel in wifi_card['channels']:
         if channel['disabled']:
             continue
         pretty_channel = "%s (%s MHz%s)" % (channel['number'],
                                             channel['frequency'], ", DFS"
                                             if channel['radar'] else "")
         if channel['frequency'] < 2500:
             channels_2g4.append((str(channel['number']), pretty_channel))
         else:
             channels_5g.append((str(channel['number']), pretty_channel))
     return channels_2g4, channels_5g
Exemple #22
0
    def get_form(self):
        time_form = fapi.ForisForm("time", self.data, filter=filters.time)
        time_main = time_form.add_section(
            name="set_time",
            title=_(self.userfriendly_title),
            description=
            _("We could not synchronize the time with a timeserver, probably due to a "
              "loss of connection. It is necessary for the router to have correct time "
              "in order to function properly. Please, synchronize it with your "
              "computer's time, or set it manually."))

        time_main.add_field(Textbox,
                            name="time",
                            label=_("Time"),
                            nuci_path="time",
                            nuci_preproc=lambda v: v.local)

        def time_form_cb(data):
            client.set_time(data['time'])
            return "none", None

        time_form.add_callback(time_form_cb)

        return time_form
Exemple #23
0
def get_messages():
    try:
        return get(filter=filters.messages).find_child(
            "messages") or user_notify.Messages()
    except (RPCError, TimeoutExpiredError):
        from foris.core import ugettext as _
        logger.exception("Unable to fetch messages")
        error_message = user_notify.Message(
            message_id=None,
            body=LocalizableTextValue(
                _("Unable to retrieve notifications, "
                  "please try refreshing the page.")),
            severity="error",
            timestamp=int(time()))
        return user_notify.Messages(messages=[error_message])
Exemple #24
0
def config_page_post(page_name):
    ConfigPage = get_config_page(page_name)
    config_page = ConfigPage(request.POST)
    if request.is_xhr:
        # only update is allowed
        return config_page.render(is_xhr=True)
    try:
        if config_page.save():
            bottle.redirect(request.fullpath)
    except TypeError:
        # raised by Validator - could happen when the form is posted with wrong fields
        messages.error(_("Configuration could not be saved due to an internal error."))
        logger.exception("Error when saving form.")
    logger.warning("Form not saved.")
    return config_page.render(config_pages=config_page_map,
                              active_config_page_key=page_name)
Exemple #25
0
def redirect_unauthenticated(redirect_url=None):
    redirect_url = redirect_url or reverse("index")
    no_auth = bottle.default_app().config.get("no_auth", False)
    if not no_auth and not is_user_authenticated():
        from foris.core import ugettext as _
        import messages
        messages.info(_("You have been logged out due to longer inactivity."))
        if bottle.request.is_xhr:
            # "raise" JSON response if requested by XHR
            res = bottle.response.copy(cls=bottle.HTTPResponse)
            res.content_type = 'application/json'
            res.body = json.dumps(dict(success=False, loggedOut=True, loginUrl=redirect_url))
            raise res
        # "raise" standard bottle redirect
        login_url = "%s?next=%s" % (redirect_url, bottle.request.fullpath)
        bottle.redirect(login_url)
Exemple #26
0
def redirect_unauthenticated(redirect_url=None):
    redirect_url = redirect_url or reverse("index")
    no_auth = bottle.default_app().config.get("no_auth", False)
    if not no_auth and not is_user_authenticated():
        from foris.core import ugettext as _
        import messages

        messages.info(_("You have been logged out due to longer inactivity."))
        if bottle.request.is_xhr:
            # "raise" JSON response if requested by XHR
            res = bottle.response.copy(cls=bottle.HTTPResponse)
            res.content_type = "application/json"
            res.body = json.dumps(dict(success=False, loggedOut=True, loginUrl=redirect_url))
            raise res
        # "raise" standard bottle redirect
        login_url = "%s?next=%s" % (redirect_url, bottle.request.fullpath)
        bottle.redirect(login_url)
Exemple #27
0
def config_action_post(page_name, action):
    ConfigPage = get_config_page(page_name)
    config_page = ConfigPage(request.POST)
    if request.is_xhr:
        # only update is allowed
        request.POST.pop("update", None)
        return config_page.render(is_xhr=True)
    # check if the button click wasn't any sub-action
    subaction = request.POST.pop("action", None)
    if subaction:
        return config_action_post(page_name, subaction)
    try:
        result = config_page.call_action(action)
        try:
            if not result:
                bottle.redirect(reverse("config_page", page_name=page_name))
        except TypeError:
            # raised by Validator - could happen when the form is posted with wrong fields
            messages.error(_("Configuration could not be saved due to an internal error."))
            logger.exception("Error when saving form.")
        logger.warning("Form not saved.")
        return result
    except ValueError:
        raise bottle.HTTPError(404, "Unknown action.")
Exemple #28
0
 def __init__(self):
     super(IPv4Prefix, self).__init__(
         _("This is not an IPv4 address with prefix length."))
Exemple #29
0
    def get_form(self):
        region_form = fapi.ForisForm(
            "region", self.data, filter=filters.create_config_filter("system"))
        timezone = region_form.add_section(
            name="timezone",
            title=_(self.userfriendly_title),
            description=_(
                "Please select the timezone the router is being operated in. "
                "Correct setting is required to display the right time and for related functions."
            ))

        lang = bottle.request.app.lang

        def construct_args(items,
                           translation_function=_,
                           key_getter=lambda x: x):
            """
            Helper function that builds args for country/timezone dropdowns.
            If there's only one item, dropdown should contain only that item.
            Otherwise the list of items should be prepended by an empty value.

            :param items: list of filtered TZ data
            :param translation_function: function that returns displayed choice from TZ data
            :param key_getter:
            :return: list of args
            """
            args = localized_sorted(
                ((key_getter(x), translation_function(x)) for x in items),
                lang=lang,
                key=lambda x: x[1])
            if len(args) > 1:
                return [(None, "-" * 16)] + args
            return args

        timezone.add_field(Hidden,
                           name="system_name",
                           nuci_path="uci.system.@system[0]",
                           nuci_preproc=lambda val: val.name)

        regions = localized_sorted(((x, _(x)) for x in tzinfo.regions),
                                   lang=lang,
                                   key=lambda x: x[1])
        timezone.add_field(Dropdown,
                           name="region",
                           label=_("Continent or ocean"),
                           required=True,
                           args=regions,
                           nuci_path="uci.system.@system[0].zonename",
                           nuci_preproc=lambda x: x.value.split("/")[0])

        # Get region and offer available countries
        region = region_form.current_data.get('region')
        countries = construct_args(tzinfo.countries_in_region(region),
                                   lambda x: _(tzinfo.countries[x]))
        timezone.add_field(Dropdown, name="country", label=_("Country"), required=True,
                           default=None, args=countries,
                           nuci_path="uci.system.@system[0].zonename",
                           nuci_preproc=lambda x: tzinfo.get_country_for_tz(x.value))\
            .requires("region")

        # Get country and offer available timezones
        country = region_form.current_data.get("country", countries[0][0])

        # It's possible that data contain country from the previous request,
        # in that case fall back to the first item in list of available countries
        if country not in (x[0] for x in countries):
            country = countries[0][0]
        timezones = construct_args(tzinfo.timezones_in_region_and_country(
            region, country),
                                   translation_function=lambda x: _(x[2]),
                                   key_getter=lambda x: x[0])

        # Offer timezones - but only if a country is selected and is not None (ensured by the
        # requires() method)
        timezone.add_field(Dropdown, name="zonename", label=_("Timezone"), required=True,
                           default=None,
                           args=timezones, nuci_path="uci.system.@system[0].zonename")\
            .requires("country", lambda x: country and x is not None)

        def region_form_cb(data):
            uci = Uci()
            system = Config("system")
            uci.add(system)
            system_section = Section(data['system_name'], "system")
            system.add(system_section)
            zonename = data['zonename']
            system_section.add(Option("zonename", zonename))
            system_section.add(
                Option("timezone", tzinfo.get_zoneinfo_for_tz(zonename)))
            return "edit_config", uci

        region_form.add_callback(region_form_cb)
        return region_form
Exemple #30
0
    def get_form(self):
        notifications_form = fapi.ForisForm(
            "notifications",
            self.data,
            filter=create_config_filter("user_notify"))

        notifications = notifications_form.add_section(
            name="notifications", title=_("Notifications settings"))
        # notifications settings
        notifications.add_field(Checkbox,
                                name="enable_smtp",
                                label=_("Enable notifications"),
                                nuci_path="uci.user_notify.smtp.enable",
                                nuci_preproc=lambda val: bool(int(val.value)),
                                default=False)

        notifications.add_field(
            Radio,
            name="use_turris_smtp",
            label=_("SMTP provider"),
            default="0",
            args=(("1", _("Turris")), ("0", _("Custom"))),
            nuci_path="uci.user_notify.smtp.use_turris_smtp",
            hint=_("If you set SMTP provider to \"Turris\", the servers provided to members of the "
                   "Turris project would be used. These servers do not require any additional "
                   "settings. If you want to set your own SMTP server, please select \"Custom\" "
                   "and enter required settings."))\
            .requires("enable_smtp", True)

        notifications.add_field(
            Textbox,
            name="to",
            label=_("Recipient's email"),
            nuci_path="uci.user_notify.smtp.to",
            nuci_preproc=lambda x: " ".join(
                map(lambda value: value.content, x.children)),
            hint=
            _("Email address of recipient. Separate multiple addresses by spaces."
              ),
            required=True).requires("enable_smtp", True)

        # sender's name for CZ.NIC SMTP only
        notifications.add_field(
            Textbox,
            name="sender_name",
            label=_("Sender's name"),
            hint=_("Name of the sender - will be used as a part of the "
                   "sender's email address before the \"at\" sign."),
            nuci_path="uci.user_notify.smtp.sender_name",
            validators=[validators.RegExp(_("Sender's name can contain only "
                                            "alphanumeric characters, dots "
                                            "and underscores."),
                                          r"^[0-9a-zA-Z_\.-]+$")],
            required=True
        )\
            .requires("enable_smtp", True)\
            .requires("use_turris_smtp", "1")

        SEVERITY_OPTIONS = (
            (1, _("Reboot is required")),
            (2, _("Reboot or attention is required")),
            (3, _("Reboot or attention is required or update was installed")),
        )
        notifications.add_field(Dropdown, name="severity", label=_("Importance"),
                                nuci_path="uci.user_notify.notifications.severity",
                                nuci_preproc=lambda val: int(val.value),
                                args=SEVERITY_OPTIONS, default=1)\
            .requires("enable_smtp", True)
        notifications.add_field(Checkbox, name="news", label=_("Send news"),
                                hint=_("Send emails about new features."),
                                nuci_path="uci.user_notify.notifications.news",
                                nuci_preproc=lambda val: bool(int(val.value)),
                                default=True)\
            .requires("enable_smtp", True)

        # SMTP settings (custom server)
        smtp = notifications_form.add_section(name="smtp",
                                              title=_("SMTP settings"))
        smtp.add_field(Email, name="from", label=_("Sender address (From)"),
                       hint=_("This is the address notifications are sent from."),
                       nuci_path="uci.user_notify.smtp.from",
                       required=True)\
            .requires("enable_smtp", True)\
            .requires("use_turris_smtp", "0")
        smtp.add_field(Textbox, name="server", label=_("Server address"),
                                nuci_path="uci.user_notify.smtp.server",
                                required=True)\
            .requires("enable_smtp", True)\
            .requires("use_turris_smtp", "0")
        smtp.add_field(Number, name="port", label=_("Server port"),
                       nuci_path="uci.user_notify.smtp.port",
                       validators=[validators.PositiveInteger()],
                       required=True) \
            .requires("enable_smtp", True)\
            .requires("use_turris_smtp", "0")

        SECURITY_OPTIONS = (
            ("none", _("None")),
            ("ssl", _("SSL/TLS")),
            ("starttls", _("STARTTLS")),
        )
        smtp.add_field(Dropdown, name="security", label=_("Security"),
                       nuci_path="uci.user_notify.smtp.security",
                       args=SECURITY_OPTIONS, default="none") \
            .requires("enable_smtp", True).requires("use_turris_smtp", "0")

        smtp.add_field(Textbox, name="username", label=_("Username"),
                       nuci_path="uci.user_notify.smtp.username")\
            .requires("enable_smtp", True)\
            .requires("use_turris_smtp", "0")
        smtp.add_field(Password, name="password", label=_("Password"),
                       nuci_path="uci.user_notify.smtp.password")\
            .requires("enable_smtp", True)\
            .requires("use_turris_smtp", "0")

        # reboot time
        reboot = notifications_form.add_section(
            name="reboot", title=_("Automatic restarts after software update"))
        reboot.add_field(
            Number,
            name="delay",
            label=_("Delay (days)"),
            hint=_(
                "Number of days that must pass between receiving the request "
                "for restart and the automatic restart itself."),
            nuci_path="uci.user_notify.reboot.delay",
            validators=[
                validators.PositiveInteger(),
                validators.InRange(0, 10)
            ],
            required=True)
        reboot.add_field(
            Time,
            name="reboot_time",
            label=_("Reboot time"),
            hint=_("Time of day of automatic reboot in HH:MM format."),
            nuci_path="uci.user_notify.reboot.time",
            validators=[validators.Time()],
            required=True)

        def notifications_form_cb(data):
            uci = Uci()
            user_notify = Config("user_notify")
            uci.add(user_notify)

            smtp = Section("smtp", "smtp")
            user_notify.add(smtp)
            smtp.add(Option("enable", data['enable_smtp']))

            reboot = Section("reboot", "reboot")
            user_notify.add(reboot)
            reboot.add(Option("time", data['reboot_time']))
            reboot.add(Option("delay", data['delay']))

            if data['enable_smtp']:
                smtp.add(Option("use_turris_smtp", data['use_turris_smtp']))
                if data['use_turris_smtp'] == "0":
                    smtp.add(Option("server", data['server']))
                    smtp.add(Option("port", data['port']))
                    smtp.add(Option("username", data['username']))
                    smtp.add(Option("password", data['password']))
                    smtp.add(Option("security", data['security']))
                    smtp.add(Option("from", data['from']))
                else:
                    smtp.add(Option("sender_name", data['sender_name']))
                to = List("to")
                for i, to_item in enumerate(data['to'].split(" ")):
                    if to_item:
                        to.add(Value(i, to_item))
                smtp.add_replace(to)
                # notifications section
                notifications = Section("notifications", "notifications")
                user_notify.add(notifications)
                notifications.add(Option("severity", data['severity']))
                notifications.add(Option("news", data['news']))

            return "edit_config", uci

        notifications_form.add_callback(notifications_form_cb)

        return notifications_form
Exemple #31
0
 def __init__(self):
     super(IPv6Prefix, self).__init__(_("This is not an IPv6 address with prefix length."))
Exemple #32
0
def index():
    notifications = client.get_messages()
    return template("config/index", config_pages=config_page_map, title=_("Home page"),
                    make_notification_title=make_notification_title,
                    active_config_page_key='',
                    notifications=notifications.new)
Exemple #33
0
 def default_template(self, **kwargs):
     if kwargs.get("stepnumber"):
         kwargs['title'] = _("Configuration wizard - step %s") % kwargs['stepnumber']
     next_step_url = reverse("wizard_step", number=self.next_step_allowed)
     return template(self.template, can_skip_wizard=self.can_skip_wizard,
                     stepname=self.name, next_step_url=next_step_url, **kwargs)
Exemple #34
0
    def get_form(self):
        form = fapi.ForisForm("enable_collection",
                              self.data,
                              filter=filters.create_config_filter(
                                  "foris", "updater"))

        section = form.add_section(
            name="collection_toggle",
            title=_(self.userfriendly_title),
        )
        section.add_field(Checkbox,
                          name="enable",
                          label=_("Enable data collection"),
                          nuci_path="uci.foris.eula.agreed_collect",
                          nuci_preproc=lambda val: bool(int(val.value)))

        def form_cb(data):
            uci = build_option_uci_tree("foris.eula.agreed_collect", "config",
                                        data.get("enable"))
            return "edit_config", uci

        def adjust_lists_cb(data):
            uci = Uci()
            # All enabled lists
            enabled_lists = map(
                lambda x: x.content,
                form.nuci_config.find_child(
                    "uci.updater.pkglists.lists").children)
            # Lists that do not need agreement
            enabled_no_agree = filter(lambda x: not x.startswith("i_agree_"),
                                      enabled_lists)
            # Lists that need agreement
            enabled_i_agree = filter(lambda x: x.startswith("i_agree_"),
                                     enabled_lists)

            # Always install lists that do not need agreement - create a copy of the list
            installed_lists = enabled_no_agree[:]
            logger.warning("no agree: %s", enabled_no_agree)
            logger.warning("installed: %s", installed_lists)
            if data.get("enable", False):
                # Include i_agree lists if user agreed with EULA
                installed_lists.extend(enabled_i_agree)
                # Add main data collection list if it's not present
                logger.warning(installed_lists)
                logger.warning("i_agree_datacollect" not in installed_lists)
                logger.warning("i_agree_datacollect" in installed_lists)
                if "i_agree_datacollect" not in installed_lists:
                    logger.warning("appending")
                    installed_lists.append("i_agree_datacollect")
            logger.warning("saving %s", installed_lists)
            # Reconstruct list of package lists
            updater = uci.add(Config("updater"))
            pkglists = updater.add(Section("pkglists", "pkglists"))
            lists = List("lists")
            for i, name in enumerate(installed_lists):
                lists.add(Value(i, name))

            # If there's anything to add, replace the list, otherwise remove it completely
            if len(installed_lists) > 0:
                pkglists.add_replace(lists)
            else:
                pkglists.add_removal(lists)

            return "edit_config", uci

        def run_updater_cb(data):
            logger.info("Checking for updates.")
            client.check_updates()
            return "none", None

        form.add_callback(form_cb)
        form.add_callback(adjust_lists_cb)
        form.add_callback(run_updater_cb)

        return form
Exemple #35
0
 def __init__(self):
     super(MacAddress, self).__init__(_("MAC address is not valid."))
     self.extra_data['parsley-validation-minlength'] = '17'
     self.reg_exp = re.compile(r"^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$")
Exemple #36
0
 def __init__(self):
     super(Domain, self).__init__(_("This is not a valid domain name."))
     self.extra_data['parsley-validation-maxlength'] = '63'
     self.reg_exp = re.compile(r"^(?![0-9]+$)(?!-)[a-zA-Z0-9-]{,63}(?<!-)$")
Exemple #37
0
 def __init__(self):
     super(PositiveInteger, self).__init__(_("Is not a number."))
Exemple #38
0
 def __init__(self):
     super(PositiveInteger, self).__init__(_("Is not a number."))
Exemple #39
0
    def get_form(self):
        lan_form = fapi.ForisForm(
            "lan", self.data, filter=create_config_filter("dhcp", "network", "firewall", "sqm"))
        lan_main = lan_form.add_section(
            name="set_lan",
            title=_(self.userfriendly_title),
            description=_("This section contains settings for the local network (LAN). The provided"
                          " defaults are suitable for most networks. <br><strong>Note:</strong> If "
                          "you change the router IP address, all computers in LAN, probably "
                          "including the one you are using now, will need to obtain a <strong>new "
                          "IP address</strong> which does <strong>not</strong> happen <strong>"
                          "immediately</strong>. It is recommended to disconnect and reconnect all "
                          "LAN cables after submitting your changes to force the update. The next "
                          "page will not load until you obtain a new IP from DHCP (if DHCP enabled)"
                          " and you might need to <strong>refresh the page</strong> in your "
                          "browser.")
        )

        lan_main.add_field(Textbox, name="lan_ipaddr", label=_("Router IP address"),
                           nuci_path="uci.network.lan.ipaddr",
                           validators=validators.IPv4(),
                           hint=_("Router's IP address in inner network. Also defines the range of "
                                  "assigned IP addresses."))
        lan_main.add_field(Checkbox, name="dhcp_enabled", label=_("Enable DHCP"),
                           nuci_path="uci.dhcp.lan.ignore",
                           nuci_preproc=lambda val: not bool(int(val.value)), default=True,
                           hint=_("Enable this option to automatically assign IP addresses to "
                                  "the devices connected to the router."))
        lan_main.add_field(Textbox, name="dhcp_min", label=_("DHCP start"),
                           nuci_path="uci.dhcp.lan.start")\
            .requires("dhcp_enabled", True)
        lan_main.add_field(Textbox, name="dhcp_max", label=_("DHCP max leases"),
                           nuci_path="uci.dhcp.lan.limit")\
            .requires("dhcp_enabled", True)

        guest_network_section = lan_form.add_section(
            name="guest_network",
            title=_("Guest network"),
        )
        guest_network_section.add_field(
            Checkbox, name="guest_network_enabled",
            label=_("Enable guest network"), default=False,
            hint=_(
                "Guest network is used for <a href='%(url)s'>guest Wi-Fi</a>. It is separated  "
                "from your ordinary LAN network. Devices connected to this network are allowed "
                "to access the internet, but are not allowed to access other devices and "
                "the configuration interface of the router."
            ) % dict(url=reverse("config_page", page_name="wifi")),
            nuci_preproc=guest_network_enabled,
        )
        guest_network_section.add_field(
            Textbox, name="guest_network_subnet", label=_("Guest network"),
            nuci_preproc=generate_network_preprocessor(
                "uci.network.guest_turris.ipaddr",
                "uci.network.guest_turris.netmask",
                DEFAULT_GUEST_NETWORK,
                DEFAULT_GUEST_MASK,
            ),
            validators=[validators.IPv4Prefix()],
            hint=_(
                "You need to set the IP range for your guest network. It is necessary that "
                "the range is different than ranges on your other networks (LAN, WAN, VPN, etc.)."
            ),
        ).requires("guest_network_enabled", True)
        guest_network_section.add_field(
            Checkbox, name="guest_network_shapping", label=_("Guest Lan QoS"),
            nuci_preproc=parse_uci_bool,
            nuci_path="uci.sqm.guest_limit_turris.enabled",
            hint=_(
                "This option enables you to set a bandwidth limit for the guest network, "
                "so that your main network doesn't get slowed-down by it."
            ),
        ).requires("guest_network_enabled", True)
        guest_network_section.add_field(
            Number,
            name="guest_network_download", label=_("Download (kb/s)"),
            validators=[validators.PositiveInteger()],
            hint=_(
                "Download speed in guest network (in kilobits per second)."
            ),
            default=1024,
            nuci_path="uci.sqm.guest_limit_turris.upload",
        ).requires("guest_network_shapping", True)
        guest_network_section.add_field(
            Number,
            name="guest_network_upload", label=_("Upload (kb/s)"),
            validators=[validators.PositiveInteger()],
            hint=_(
                "Upload speed in guest network (in kilobits per second)."
            ),
            default=1024,
            nuci_path="uci.sqm.guest_limit_turris.download",
        ).requires("guest_network_shapping", True)

        def lan_form_cb(data):
            uci = Uci()
            config = Config("dhcp")
            uci.add(config)

            dhcp = Section("lan", "dhcp")
            config.add(dhcp)
            # FIXME: this would overwrite any unrelated DHCP options the user might have set.
            # Maybe we should get the current values, scan them and remove selectively the ones
            # with 6 in front of them? Or have some support for higher level of stuff in nuci.
            options = List("dhcp_option")
            options.add(Value(0, "6," + data['lan_ipaddr']))
            dhcp.add_replace(options)
            network = Config("network")
            uci.add(network)
            interface = Section("lan", "interface")
            network.add(interface)
            interface.add(Option("ipaddr", data['lan_ipaddr']))
            if data['dhcp_enabled']:
                dhcp.add(Option("ignore", "0"))
                dhcp.add(Option("start", data['dhcp_min']))
                dhcp.add(Option("limit", data['dhcp_max']))
            else:
                dhcp.add(Option("ignore", "1"))

            # qos data
            qos = {'enabled': False}
            if 'guest_network_shapping' in data and data['guest_network_shapping']:
                qos['enabled'] = True
                qos['download'] = data['guest_network_download']
                qos['upload'] = data['guest_network_upload']

            # update guest network configs
            guest_enabled = data.get("guest_network_enabled")
            guest_network_subnet = data.get("guest_network_subnet")
            if guest_network_subnet:
                network, prefix = data.get("guest_network_subnet").split("/")
            else:
                network, prefix = DEFAULT_GUEST_NETWORK, DEFAULT_GUEST_PREFIX

            # disable guest wifi when guest network is not enabled
            data = client.get(filter=wifi_filter())
            card_count = 0
            while data.find_child("uci.wireless.@wifi-device[%d]" % card_count):
                card_count += 1
            if not guest_enabled and card_count > 0:
                wireless = uci.add(Config("wireless"))
                for i in range(card_count):
                    guest_iface = wireless.add(Section("guest_iface_%d" % i, "wifi-iface"))
                    guest_iface.add(Option("disabled", "1"))

            guest_interfaces = ["guest_turris_%d" % e for e in range(card_count)]

            LanHandler.prepare_guest_configs(
                uci, guest_enabled, network, prefix, guest_interfaces, qos)

            return "edit_config", uci

        lan_form.add_callback(lan_form_cb)

        return lan_form
Exemple #40
0
 def __init__(self):
     super(IPv4, self).__init__(_("Not a valid IPv4 address."))
Exemple #41
0
 def __init__(self):
     super(IPv4Netmask,
           self).__init__(_("Not a valid IPv4 netmask address."))
Exemple #42
0
 def __init__(self, low, high):
     self._low = low
     self._high = high
     super(ByteLenRange, self).__init__(_("Length must be from %(low)s to %(high)s characters.")
                                        % dict(low=low, high=high))
     self.js_validator_params = "[%s,%s]" % (low, high)
Exemple #43
0
 def default_template(self, **kwargs):
     return template(self.template, title=_(kwargs.pop('title', self.userfriendly_title)),
                     **kwargs)
Exemple #44
0
    def get_form(self):
        ucollect_form = fapi.ForisForm(
            "ucollect",
            self.data,
            filter=filters.create_config_filter("ucollect"))
        fakes = ucollect_form.add_section(
            name="fakes",
            title=_("Emulated services"),
            description=
            _("One of uCollect's features is emulation of some commonly abused "
              "services. If this function is enabled, uCollect is listening for "
              "incoming connection attempts to these services. Enabling of the "
              "emulated services has no effect if another service is already "
              "listening on its default port (port numbers are listed below)."
              ))

        SERVICES_OPTIONS = (
            ("23tcp", _("Telnet (23/TCP)")),
            ("2323tcp", _("Telnet - alternative port (2323/TCP)")),
            ("80tcp", _("HTTP (80/TCP)")),
            ("3128tcp", _("Squid HTTP proxy (3128/TCP)")),
            ("8123tcp", _("Polipo HTTP proxy (8123/TCP)")),
            ("8080tcp", _("HTTP proxy (8080/TCP)")),
        )

        def get_enabled_services(disabled_list):
            disabled_services = map(lambda value: value.content,
                                    disabled_list.children)
            res = [
                x[0] for x in SERVICES_OPTIONS if x[0] not in disabled_services
            ]
            return res

        fakes.add_field(MultiCheckbox,
                        name="services",
                        label=_("Emulated services"),
                        args=SERVICES_OPTIONS,
                        multifield=True,
                        nuci_path="uci.ucollect.fakes.disable",
                        nuci_preproc=get_enabled_services,
                        default=[x[0] for x in SERVICES_OPTIONS])

        fakes.add_field(
            Checkbox,
            name="log_credentials",
            label=_("Collect credentials"),
            hint=
            _("If this option is enabled, user names and passwords are collected "
              "and sent to server in addition to the IP address of the client."
              ),
            nuci_path="uci.ucollect.fakes.log_credentials",
            nuci_preproc=parse_uci_bool)

        def ucollect_form_cb(data):
            uci = Uci()
            ucollect = Config("ucollect")
            uci.add(ucollect)

            fakes = Section("fakes", "fakes")
            ucollect.add(fakes)

            disable = List("disable")

            disabled_services = [
                x[0] for x in SERVICES_OPTIONS if x[0] not in data['services']
            ]
            for i, service in enumerate(disabled_services):
                disable.add(Value(i, service))

            if len(disabled_services):
                fakes.add_replace(disable)
            else:
                fakes.add_removal(disable)

            fakes.add(Option("log_credentials", data['log_credentials']))

            return "edit_config", uci

        ucollect_form.add_callback(ucollect_form_cb)

        return ucollect_form
Exemple #45
0
 def __init__(self):
     super(AnyIP, self).__init__(_("This is not a valid IPv4 or IPv6 address."))
Exemple #46
0
 def __init__(self):
     super(IPv4, self).__init__(_("Not a valid IPv4 address."))
Exemple #47
0
 def __init__(self):
     super(IPv4Netmask, self).__init__(_("Not a valid IPv4 netmask address."))
Exemple #48
0
 def __init__(self):
     super(MacAddress, self).__init__(_("MAC address is not valid."))
     self.extra_data['parsley-validation-minlength'] = '17'
     self.reg_exp = re.compile(r"^([a-fA-F0-9]{2}:){5}[a-fA-F0-9]{2}$")
Exemple #49
0
    def get_form(self):
        # form definitions
        pw_form = fapi.ForisForm("password", self.data)
        pw_main = pw_form.add_section(
            name="set_password",
            title=_(self.userfriendly_title),
            description=_("Set your password for this administration "
                          "interface. The password must be at least 6 "
                          "characters long."))
        if self.change:
            pw_main.add_field(Password,
                              name="old_password",
                              label=_("Current password"))
            label_pass1 = _("New password")
            label_pass2 = _("New password (repeat)")
        else:
            label_pass1 = _("Password")
            label_pass2 = _("Password (repeat)")

        pw_main.add_field(Password,
                          name="password",
                          label=label_pass1,
                          required=True,
                          validators=validators.LenRange(6, 128))
        pw_main.add_field(Password,
                          name="password_validation",
                          label=label_pass2,
                          required=True,
                          validators=validators.EqualTo(
                              "password", "password_validation",
                              _("Passwords are not equal.")))
        pw_main.add_field(
            Checkbox,
            name="set_system_pw",
            label=_("Use the same password for advanced configuration"),
            hint=_(
                "Same password would be used for accessing this administration "
                "interface, for root user in LuCI web interface and for SSH "
                "login. Use a strong password! (If you choose not to set the "
                "password for advanced configuration here, you will have the "
                "option to do so later. Until then, the root account will be "
                "blocked.)"))

        def pw_form_cb(data):
            from beaker.crypto import pbkdf2
            if self.change:
                # if changing password, check the old pw is right first
                uci_data = client.get(filter=filters.foris_config)
                password_hash = uci_data.find_child("uci.foris.auth.password")
                # allow changing the password if password_hash is empty
                if password_hash:
                    password_hash = password_hash.value
                    # crypt automatically extracts salt and iterations from formatted pw hash
                    if password_hash != pbkdf2.crypt(data['old_password'],
                                                     salt=password_hash):
                        return "save_result", {'wrong_old_password': True}

            uci = Uci()
            foris = Config("foris")
            uci.add(foris)
            auth = Section("auth", "config")
            foris.add(auth)
            # use 48bit pseudo-random salt internally generated by pbkdf2
            new_password_hash = pbkdf2.crypt(data['password'], iterations=1000)
            auth.add(Option("password", new_password_hash))

            if data['set_system_pw'] is True:
                client.set_password("root", data['password'])

            return "edit_config", uci

        pw_form.add_callback(pw_form_cb)
        return pw_form
Exemple #50
0
 def __init__(self):
     pattern = r"^([01][0-9]|2[0-3]):([0-5][0-9])$"
     super(Time, self).__init__(_("This is not valid time in HH:MM format."), pattern)
     self.js_validator = ("pattern", pattern)
     self.extra_data['parsley-error-message'] = self.msg
Exemple #51
0
    def get_form(self):
        dns_form = fapi.ForisForm("dns",
                                  self.data,
                                  filter=create_config_filter(
                                      "resolver", "dhcp"))
        dns_main = dns_form.add_section(name="set_dns",
                                        title=_(self.userfriendly_title))
        dns_main.add_field(Checkbox,
                           name="forward_upstream",
                           label=_("Use forwarding"),
                           nuci_path="uci.resolver.common.forward_upstream",
                           nuci_preproc=lambda val: bool(int(val.value)),
                           default=True)

        if not contract_valid():
            dns_main.add_field(
                Checkbox,
                name="ignore_root_key",
                label=_("Disable DNSSEC"),
                nuci_path="uci.resolver.common.ignore_root_key",
                nuci_preproc=lambda val: bool(int(val.value)),
                default=False,
            )

        resolver = dns_form.nuci_config.find_child(
            "uci.resolver.common.prefered_resolver")
        if resolver and resolver.value in ["kresd", "unbound"]:
            dns_main.add_field(
                Checkbox,
                name="dhcp_from_dns",
                label=_("Enable DHCP clients in DNS"),
                hint=_(
                    "This will enable your DNS resolver to place DHCP client's "
                    "names among the local DNS records."),
                nuci_path="uci.resolver.common.dynamic_domains",
                nuci_preproc=lambda val: bool(int(val.value)),
                default=False,
            )
            dns_main.add_field(
                Textbox,
                name="dhcp_dns_domain",
                label=_("Domain of DHCP clients in DNS"),
                hint=_(
                    "This domain will be used as TLD. E.g. The result for client \"android-123\" "
                    "and domain \"lan\" will be \"android-123.lan\"."),
                nuci_path="uci.dhcp.@dnsmasq[0].local",
                nuci_preproc=lambda val: val.value.strip("/")
                if val else "lan",
                default="lan",
                validators=[validators.Domain()],
            ).requires("dhcp_from_dns", True)

        resolver = dns_form.nuci_config.find_child("uci.dhcp.@dnsmasq[0]")

        def dns_form_cb(data):
            uci = Uci()
            resolver = Config("resolver")
            uci.add(resolver)
            server = Section("common", "resolver")
            resolver.add(server)
            server.add(Option("forward_upstream", data['forward_upstream']))
            if not contract_valid():
                server.add(Option("ignore_root_key", data['ignore_root_key']))

            if 'dhcp_from_dns' in data:
                server.add(Option("dynamic_domains", data['dhcp_from_dns']))

            if 'dhcp_dns_domain' in data:
                dhcp = uci.add(Config("dhcp"))
                dnsmasq_section = dns_form.nuci_config.find_child(
                    "uci.dhcp.@dnsmasq[0]")
                dnsmasq = dhcp.add(
                    Section(dnsmasq_section.name, "dnsmasq", anonymous=True))
                dnsmasq.add(
                    Option("local",
                           "/%s/" % data["dhcp_dns_domain"].strip("/")))

            return "edit_config", uci

        dns_form.add_callback(dns_form_cb)
        return dns_form
Exemple #52
0
 def __init__(self):
     super(AnyIP,
           self).__init__(_("This is not a valid IPv4 or IPv6 address."))
Exemple #53
0
 def render(self, **kwargs):
     stats = client.get(filter=filters.stats).find_child("stats")
     if_eth2 = stats.data['interfaces'].get('eth2')
     if not (if_eth2 and if_eth2.get('is_up')):
         messages.warning(_("WAN port has no link, your internet connection probably won't work."))
     return super(WanConfigPage, self).render(**kwargs)
Exemple #54
0
 def __init__(self, low, high):
     self._low = low
     self._high = high
     super(InRange, self).__init__(_("Not in a valid range %(low)s - %(high)s.")
                                   % dict(low=low, high=high))
     self.js_validator_params = "[%s,%s]" % (low, high)
Exemple #55
0
    def get_form(self):
        # WAN
        wan_form = fapi.ForisForm("wan",
                                  self.data,
                                  filter=create_config_filter(
                                      "network", "smrtd", "ucollect"))
        wan_main = wan_form.add_section(
            name="set_wan",
            title=_(self.userfriendly_title),
            description=
            _("Here you specify your WAN port settings. Usually, you can leave this "
              "options untouched unless instructed otherwise by your internet service "
              "provider. Also, in case there is a cable or DSL modem connecting your "
              "router to the network, it is usually not necessary to change this "
              "setting."))
        WAN_DHCP = "dhcp"
        WAN_STATIC = "static"
        WAN_PPPOE = "pppoe"
        WAN_OPTIONS = (
            (WAN_DHCP, _("DHCP (automatic configuration)")),
            (WAN_STATIC, _("Static IP address (manual configuration)")),
            (WAN_PPPOE, _("PPPoE (for DSL bridges, Modem Turris, etc.)")),
        )

        WAN6_NONE = "none"
        WAN6_DHCP = "dhcpv6"
        WAN6_STATIC = "static"

        WAN6_OPTIONS = (
            (WAN6_DHCP, _("DHCPv6 (automatic configuration)")),
            (WAN6_STATIC, _("Static IP address (manual configuration)")),
        )

        if not self.hide_no_wan:
            WAN6_OPTIONS = ((WAN6_NONE, _("Disable IPv6")), ) + WAN6_OPTIONS

        # protocol
        wan_main.add_field(Dropdown,
                           name="proto",
                           label=_("IPv4 protocol"),
                           nuci_path="uci.network.wan.proto",
                           args=WAN_OPTIONS,
                           default=WAN_DHCP)

        # static ipv4
        wan_main.add_field(Textbox, name="ipaddr", label=_("IP address"),
                           nuci_path="uci.network.wan.ipaddr",
                           required=True, validators=validators.IPv4())\
            .requires("proto", WAN_STATIC)
        wan_main.add_field(Textbox, name="netmask", label=_("Network mask"),
                           nuci_path="uci.network.wan.netmask",
                           required=True, validators=validators.IPv4Netmask())\
            .requires("proto", WAN_STATIC)
        wan_main.add_field(Textbox, name="gateway", label=_("Gateway"),
                           nuci_path="uci.network.wan.gateway",
                           validators=validators.IPv4(),
                           required=True)\
            .requires("proto", WAN_STATIC)

        def extract_dns_item(dns_option, index, default=None):
            if isinstance(dns_option, List):
                dns_list = [e.content for e in dns_option.children]
            elif isinstance(dns_option, Option):
                dns_list = dns_option.value.split(" ")
            else:
                return default

            # Server with higher priority should be last
            dns_list.reverse()

            try:
                return dns_list[index]
            except IndexError:
                return default

        # DNS servers
        wan_main.add_field(Textbox, name="dns1", label=_("DNS server 1"),
                           nuci_path="uci.network.wan.dns",
                           nuci_preproc=lambda val: extract_dns_item(val, 0),
                           validators=validators.AnyIP(),
                           hint=_("DNS server address is not required as the built-in "
                                  "DNS resolver is capable of working without it."))\
            .requires("proto", WAN_STATIC)
        wan_main.add_field(Textbox, name="dns2", label=_("DNS server 2"),
                           nuci_path="uci.network.wan.dns",
                           nuci_preproc=lambda val: extract_dns_item(val, 1),
                           validators=validators.AnyIP(),
                           hint=_("DNS server address is not required as the built-in "
                                  "DNS resolver is capable of working without it."))\
            .requires("proto", WAN_STATIC)

        # xDSL settings
        wan_main.add_field(Textbox, name="username", label=_("PAP/CHAP username"),
                           nuci_path="uci.network.wan.username")\
            .requires("proto", WAN_PPPOE)
        wan_main.add_field(Textbox, name="password", label=_("PAP/CHAP password"),
                           nuci_path="uci.network.wan.password")\
            .requires("proto", WAN_PPPOE)

        # IPv6 configuration
        wan_main.add_field(Dropdown,
                           name="wan6_proto",
                           label=_("IPv6 protocol"),
                           args=WAN6_OPTIONS,
                           default=WAN6_NONE,
                           nuci_path="uci.network.wan6.proto")
        wan_main.add_field(Textbox, name="ip6addr", label=_("IPv6 address"),
                           nuci_path="uci.network.wan6.ip6addr",
                           validators=validators.IPv6Prefix(),
                           hint=_("IPv6 address and prefix length for WAN interface, "
                                  "e.g. 2001:db8:be13:37da::1/64"),
                           required=True)\
            .requires("wan6_proto", WAN6_STATIC)
        wan_main.add_field(Textbox, name="ip6gw", label=_("IPv6 gateway"),
                           validators=validators.IPv6(),
                           nuci_path="uci.network.wan6.ip6gw")\
            .requires("wan6_proto", WAN6_STATIC)
        wan_main.add_field(Textbox, name="ip6prefix", label=_("IPv6 prefix"),
                           validators=validators.IPv6Prefix(),
                           nuci_path="uci.network.wan6.ip6prefix",
                           hint=_("Address range for local network, "
                                  "e.g. 2001:db8:be13:37da::/64"))\
            .requires("wan6_proto", WAN6_STATIC)

        # enable SMRT settings only if smrtd config is present
        has_smrtd = wan_form.nuci_config.find_child("uci.smrtd") is not None

        if has_smrtd:
            wan_main.add_field(Hidden, name="has_smrtd", default="1")
            wan_main.add_field(Checkbox, name="use_smrt", label=_("Use Modem Turris"),
                               nuci_path="uci.smrtd.global.enabled",
                               nuci_preproc=lambda val: bool(int(val.value)),
                               hint=_("Modem Turris (aka SMRT - Small Modem for Router Turris), "
                                      "a simple ADSL/VDSL modem designed specially for router "
                                      "Turris. Enable this option if you have Modem Turris "
                                      "connected to your router."))\
                .requires("proto", WAN_PPPOE)

            def get_smrtd_param(param_name):
                """Helper function for getting SMRTd params for "connections" list."""
                def wrapped(conn_list):
                    # internet connection must be always first list element
                    vlan_id, vpi, vci = (
                        conn_list.children[0].content.split(" ")
                        if conn_list else (None, None, None))
                    if param_name == "VPI":
                        return vpi
                    elif param_name == "VCI":
                        return vci
                    elif param_name == "VLAN":
                        return vlan_id
                    raise ValueError("Unknown SMRTd connection parameter.")

                return wrapped

            def get_smrtd_vlan(data):
                """Helper function for getting VLAN number from Uci data."""
                ifname = data.find_child("uci.network.wan.ifname")
                if ifname:
                    ifname = ifname.value
                    matches = re.match("%s.(\d+)" % self.wan_ifname, ifname)
                    if matches:
                        return matches.group(1)

                connections = data.find_child("uci.smrtd.%s.connections" %
                                              self.wan_ifname)
                result = get_smrtd_param("VLAN")(connections)
                return result

            # 802.1Q VLAN number is 12-bit, 0x0 and 0xFFF reserved
            wan_main.add_field(Textbox, name="smrt_vlan", label=_("xDSL VLAN number"),
                               nuci_preproc=get_smrtd_vlan,
                               validators=[validators.PositiveInteger(),
                                           validators.InRange(1, 4095)],
                               hint=_("VLAN number for your internet connection. Your ISP might "
                                      "have provided you this number. If you have VPI and VCI "
                                      "numbers instead, leave this field empty, a default value "
                                      "will be used automatically."))\
                .requires("use_smrt", True)

            vpi_vci_validator = validators.RequiredWithOtherFields(
                ("smrt_vpi", "smrt_vci"),
                _("Both VPI and VCI must be filled or both must be empty."))

            wan_main.add_field(
                Textbox, name="smrt_vpi", label=_("VPI"),
                nuci_path="uci.smrtd.%s.connections" % self.wan_ifname,
                nuci_preproc=get_smrtd_param("VPI"),
                validators=[validators.PositiveInteger(),
                            validators.InRange(0, 255),
                            vpi_vci_validator],
                hint=_("Virtual Path Identifier (VPI) is a parameter that you might have received "
                       "from your ISP. If you have a VLAN number instead, leave this field empty. "
                       "You need to fill in both VPI and VCI together.")
            ) \
                .requires("use_smrt", True)
            wan_main.add_field(
                Textbox, name="smrt_vci", label=_("VCI"),
                nuci_path="uci.smrtd.%s.connections" % self.wan_ifname,
                nuci_preproc=get_smrtd_param("VCI"),
                validators=[validators.PositiveInteger(),
                            validators.InRange(32, 65535),
                            vpi_vci_validator],
                hint=_("Virtual Circuit Identifier (VCI) is a parameter that you might have "
                       "received from your ISP. If you have a VLAN number instead, leave this "
                       "field empty. You need to fill in both VPI and VCI together.")
            )\
                .requires("use_smrt", True)

        # custom MAC
        wan_main.add_field(
            Checkbox,
            name="custom_mac",
            label=_("Custom MAC address"),
            nuci_path="uci.network.wan.macaddr",
            nuci_preproc=lambda val: bool(val.value),
            hint=_(
                "Useful in cases, when a specific MAC address is required by "
                "your internet service provider."))

        wan_main.add_field(Textbox, name="macaddr", label=_("MAC address"),
                           nuci_path="uci.network.wan.macaddr",
                           validators=validators.MacAddress(),
                           hint=_("Separator is a colon, for example 00:11:22:33:44:55"),
                           required=True)\
            .requires("custom_mac", True)

        def wan_form_cb(data):
            uci = Uci()
            network = Config("network")
            uci.add(network)

            wan = Section("wan", "interface")
            network.add(wan)

            wan.add(Option("proto", data['proto']))
            if data['custom_mac'] is True:
                wan.add(Option("macaddr", data['macaddr']))
            else:
                wan.add_removal(Option("macaddr", None))

            ucollect_ifname = self.wan_ifname

            if data['proto'] == WAN_PPPOE:
                wan.add(Option("username", data['username']))
                wan.add(Option("password", data['password']))
                wan.add(Option("ipv6",
                               data.get("wan6_proto") is not WAN6_NONE))
                ucollect_ifname = "pppoe-wan"
            elif data['proto'] == WAN_STATIC:
                wan.add(Option("ipaddr", data['ipaddr']))
                wan.add(Option("netmask", data['netmask']))
                wan.add(Option("gateway", data['gateway']))
                dns_list = List("dns")
                dns2 = data.get("dns2", None)
                if dns2:
                    dns_list.add(Value(0, dns2))
                dns1 = data.get("dns1", None)
                if dns1:
                    dns_list.add(Value(
                        1,
                        dns1))  # dns with higher priority should be added last
                if not dns_list.children:
                    wan.add_removal(dns_list)
                else:
                    wan.add_replace(dns_list)

            # IPv6 configuration
            wan6 = Section("wan6", "interface")
            network.add(wan6)
            wan6.add(Option("ifname", "@wan"))
            wan6.add(Option("proto", data['wan6_proto']))

            if data.get("wan6_proto") == WAN6_STATIC:
                wan6.add(Option("ip6addr", data['ip6addr']))
                wan6.add(Option("ip6gw", data['ip6gw']))
                wan6.add(Option("ip6prefix", data['ip6prefix']))
            else:
                wan6.add_removal(Option("ip6addr", None))
                wan6.add_removal(Option("ip6gw", None))
                wan6.add_removal(Option("ip6prefix", None))

            if has_smrtd:
                smrtd = Config("smrtd")
                uci.add(smrtd)

                smrt_vlan = data.get("smrt_vlan")
                use_smrt = data.get("use_smrt", False)

                wan_if = Section(self.wan_ifname, "interface")
                smrtd.add(wan_if)
                wan_if.add(Option("name", self.wan_ifname))

                if use_smrt:
                    if not smrt_vlan:
                        # "proprietary" number - and also a common VLAN ID in CZ
                        smrt_vlan = "848"
                        self.wan_ifname += ".%s" % smrt_vlan

                vpi, vci = data.get("smrt_vpi"), data.get("smrt_vci")
                connections = List("connections")
                if vpi and vci:
                    wan_if.add(connections)
                    connections.add(
                        Value(1, "%s %s %s" % (smrt_vlan, vpi, vci)))
                elif use_smrt:
                    wan_if.add_removal(connections)

                smrtd_global = Section("global", "global")
                smrtd.add(smrtd_global)
                smrtd_global.add(Option("enabled", use_smrt))

                # set correct ifname for WAN - must be changed when disabling SMRT
                wan.add(Option("ifname", self.wan_ifname))

            # set interface for ucollect to listen on
            interface_if_name = None
            ucollect_interface0 = wan_form.nuci_config.find_child(
                "uci.ucollect.@interface[0]")
            if ucollect_interface0:
                interface_if_name = ucollect_interface0.name

            ucollect = Config("ucollect")
            uci.add(ucollect)
            interface = Section(interface_if_name, "interface", True)
            ucollect.add(interface)
            interface.add(Option("ifname", ucollect_ifname))

            return "edit_config", uci

        wan_form.add_callback(wan_form_cb)

        return wan_form
Exemple #56
0
 def __init__(self):
     super(NotEmpty, self).__init__(_("This field is required."))
Exemple #57
0
    def get_form(self):
        stats = client.get(filter=filters.stats).find_child("stats")
        cards = self._get_wireless_cards(stats)

        if not cards:
            return None

        wifi_form = fapi.ForisForm("wifi",
                                   self.data,
                                   filter=filters.wifi_filter())

        # Create mapping of radio_name -> iface_index
        radio_to_iface = self._get_radio_to_iface(wifi_form)

        # Add header section (used for page title)
        wifi_form.add_section(
            name="wifi",
            title=_(self.userfriendly_title),
            description=
            _("If you want to use your router as a Wi-Fi access point, enable Wi-Fi "
              "here and fill in an SSID (the name of the access point) and a "
              "corresponding password. You can then set up your mobile devices, "
              "using the QR code available next to the form."))

        # Add wifi section
        wifi_section = wifi_form.add_section(
            name="wifi_settings",
            title=_("Wi-Fi settings"),
        )

        # Get list of available radios
        radios = self._get_radios(cards, wifi_section, radio_to_iface,
                                  self.data or {}, wifi_form.nuci_config())

        def wifi_form_cb(data):
            uci = Uci()
            wireless = Config("wireless")
            uci.add(wireless)

            guest_wifi_enabled = False
            for radio in radios:
                if self._prepare_radio_cb(data, wireless, radio):
                    guest_wifi_enabled = True

            guest_interfaces = ["guest_turris_%s" % e for e in sorted(radios)]

            # test whether it is required to pass update guest network
            current_data = client.get(filter=filters.wifi_filter())
            current_enabled = preprocessors.guest_network_enabled(current_data)
            if guest_wifi_enabled and not current_enabled:
                # Guest network handling
                guest_network_subnet = data.get("guest_network_subnet")
                if guest_network_subnet:
                    network, prefix = data.get("guest_network_subnet").split(
                        "/")
                else:
                    network, prefix = DEFAULT_GUEST_NETWORK, DEFAULT_GUEST_PREFIX
                LanHandler.prepare_guest_configs(uci, True, network, prefix,
                                                 guest_interfaces)
            elif guest_wifi_enabled:
                # try to update guest interfaces if the differs
                stored = current_data.find_child(
                    "uci.network.guest_turris.ifname")
                if not stored or set(
                        stored.value.split(" ")) != set(guest_interfaces):
                    network_conf = uci.add(Config("network"))
                    interface_section = network_conf.add(
                        Section("guest_turris", "interface"))
                    interface_section.add(
                        Option("ifname", " ".join(guest_interfaces)))

            return "edit_config", uci

        wifi_form.add_callback(wifi_form_cb)

        return wifi_form
Exemple #58
0
 def __init__(self):
     super(NotEmpty, self).__init__(_("This field is required."))
Exemple #59
0
    def _add_wifi_section(self,
                          wifi_section,
                          wifi_card,
                          radio_to_iface,
                          post_data,
                          nuci_data,
                          last=False):
        HINTS = {
            'password':
            _("WPA2 pre-shared key, that is required to connect to the "
              "network. Minimum length is 8 characters.")
        }

        radio_index = int(wifi_card['name'][3:])
        iface_index = radio_to_iface.get('radio%s' % radio_index)
        if iface_index is None:
            # Interface is not present in the wireless config - skip this radio
            return None

        def prefixed_name(name):
            return "radio%s-%s" % (radio_index, name)

        wifi_main = wifi_section.add_section(
            name=prefixed_name("set_wifi"),
            title=None,
        )

        wifi_main.add_field(Hidden,
                            name=prefixed_name("iface_section"),
                            nuci_path="uci.wireless.@wifi-iface[%s]" %
                            iface_index,
                            nuci_preproc=lambda val: val.name)

        # Use numbering starting from one. In rare cases, it may happen that the first radio
        # is not radio0, or that there's a gap between radio numbers, but it should not happen
        # on most of the setups.
        wifi_main.add_field(Checkbox,
                            name=prefixed_name("wifi_enabled"),
                            label=_("Enable Wi-Fi %s") % (radio_index + 1),
                            default=True,
                            nuci_path="uci.wireless.@wifi-iface[%s].disabled" %
                            iface_index,
                            nuci_preproc=lambda val: not bool(int(val.value)))
        wifi_main.add_field(
            Textbox,
            name=prefixed_name("ssid"),
            label=_("SSID"),
            nuci_path="uci.wireless.@wifi-iface[%s].ssid" % iface_index,
            required=True,
            validators=validators.ByteLenRange(1, 32)).requires(
                prefixed_name("wifi_enabled"), True)

        wifi_main.add_field(
            Checkbox,
            name=prefixed_name("ssid_hidden"),
            label=_("Hide SSID"),
            default=False,
            nuci_path="uci.wireless.@wifi-iface[%s].hidden" % iface_index,
            hint=
            _("If set, network is not visible when scanning for available networks."
              )).requires(prefixed_name("wifi_enabled"), True)

        channels_2g4, channels_5g = self._get_channels(wifi_card)

        is_dual_band = False
        # hwmode choice for dual band devices
        if len(channels_2g4) > 1 and len(channels_5g) > 1:
            is_dual_band = True
            wifi_main.add_field(
                Radio,
                name=prefixed_name("hwmode"),
                label=_("Wi-Fi mode"),
                default="11g",
                args=(("11g", "2.4 GHz (g)"), ("11a", "5 GHz (a)")),
                nuci_path="uci.wireless.radio%s.hwmode" % radio_index,
                nuci_preproc=lambda x: x.value.replace("n", ""
                                                       ),  # old configs used
                # 11ng/11na
                hint=
                _("The 2.4 GHz band is more widely supported by clients, but "
                  "tends to have more interference. The 5 GHz band is a newer"
                  " standard and may not be supported by all your devices. It "
                  "usually has less interference, but the signal does not "
                  "carry so well indoors.")).requires(
                      prefixed_name("wifi_enabled"), True)

        htmodes = (("NOHT", _("Disabled")),
                   ("HT20", _("802.11n - 20 MHz wide channel")),
                   ("HT40", _("802.11n - 40 MHz wide channel")))

        # Allow VHT modes only if the card has the capabilities and 5 GHz band is selected
        hwmode = self._get_value(post_data, nuci_data,
                                 "radio%s-hwmode" % radio_index,
                                 "uci.wireless.radio%s.hwmode" % radio_index)
        allow_vht = wifi_card['vht-capabilities'] and hwmode == "11a"

        if allow_vht:
            htmodes += (
                ("VHT20", _("802.11ac - 20 MHz wide channel")),
                ("VHT40", _("802.11ac - 40 MHz wide channel")),
                ("VHT80", _("802.11ac - 80 MHz wide channel")),
            )

        wifi_main.add_field(
            Dropdown,
            name=prefixed_name("htmode"),
            label=_("802.11n/ac mode"),
            args=htmodes,
            nuci_path="uci.wireless.radio%s.htmode" % radio_index,
            hint=
            _("Change this to adjust 802.11n/ac mode of operation. 802.11n with 40 MHz wide "
              "channels can yield higher throughput but can cause more interference in the "
              "network. If you don't know what to choose, use the default option with 20 MHz "
              "wide channel.")).requires(prefixed_name("wifi_enabled"), True)

        # 2.4 GHz channels
        if len(channels_2g4) > 1:
            field_2g4 = wifi_main.add_field(
                Dropdown,
                name=prefixed_name("channel2g4"),
                label=_("Network channel"),
                default=channels_2g4[0][0],
                args=channels_2g4,
                nuci_path="uci.wireless.radio%s.channel" %
                radio_index).requires(prefixed_name("wifi_enabled"), True)

            if is_dual_band:
                field_2g4.requires(prefixed_name("hwmode"), "11g")

        # 5 GHz channels
        if len(channels_5g) > 1:
            field_5g = wifi_main.add_field(
                Dropdown,
                name=prefixed_name("channel5g"),
                label=_("Network channel"),
                default=channels_5g[0][0],
                args=channels_5g,
                nuci_path="uci.wireless.radio%s.channel" %
                radio_index).requires(prefixed_name("wifi_enabled"), True)

            if is_dual_band:
                field_5g.requires(prefixed_name("hwmode"), "11a")

        wifi_main.add_field(
            Password,
            name=prefixed_name("key"),
            label=_("Network password"),
            nuci_path="uci.wireless.@wifi-iface[%s].key" % iface_index,
            required=True,
            validators=validators.ByteLenRange(8, 63),
            hint=HINTS['password']).requires(prefixed_name("wifi_enabled"),
                                             True)

        # Guest wi-fi part
        guest_section = wifi_main.add_section(
            name=prefixed_name("set_guest_wifi"),
            title=_("Guest Wi-Fi"),
            description=_("Set guest Wi-Fi here."))

        guest_section.add_field(
            Checkbox,
            name=prefixed_name("guest_enabled"),
            label=_("Enable guest Wi-Fi"),
            default=False,
            nuci_path="uci.wireless.guest_iface_%s.disabled" % iface_index,
            nuci_preproc=lambda value: not parse_uci_bool(value),
            hint=
            _("Enables Wi-Fi for guests, which is separated from LAN network. Devices "
              "connected to this network are allowed to access the internet, but aren't "
              "allowed to access other devices and the configuration interface of the router. "
              "Parameters of the guest network can be set in <a href='%(url)s'>the LAN tab</a>. "
              ) % dict(url=reverse("config_page", page_name="lan"))).requires(
                  prefixed_name("wifi_enabled"), True)

        default_ssid = self._get_value(
            post_data,
            nuci_data,
            prefixed_name("ssid"),
            "uci.wireless.@wifi-iface[%s].ssid" % iface_index,
            "Turris",
        )
        default_guest_ssid = self._get_value(
            post_data, nuci_data, prefixed_name("guest_ssid"),
            "uci.wireless.guest_iface_%s.ssid" % iface_index,
            "%s-guest" % default_ssid)
        guest_section.add_field(Textbox,
                                name=prefixed_name("guest_ssid"),
                                label=_("SSID for guests"),
                                nuci_path="uci.wireless.guest_iface_%s.ssid" %
                                iface_index,
                                required=True,
                                validators=validators.ByteLenRange(1, 32),
                                default=default_guest_ssid).requires(
                                    prefixed_name("guest_enabled"), True)

        guest_section.add_field(
            Password,
            name=prefixed_name("guest_key"),
            label=_("Password for guests"),
            nuci_path="uci.wireless.guest_iface_%s.key" % iface_index,
            required=True,
            default="",
            validators=validators.ByteLenRange(8, 63),
            hint=HINTS['password'],
        ).requires(prefixed_name("guest_enabled"), True)

        # Horizontal line separating wi-fi cards
        if not last:
            wifi_main.add_field(HorizontalLine,
                                name=prefixed_name("wifi-separator"),
                                class_="wifi-separator").requires(
                                    prefixed_name("wifi_enabled"), True)
Exemple #60
0
    def get_form(self):
        def approval_preproc_approve_status(nuci_config):
            """Preprocess approval status """
            # try to obtain status from the form data
            if self.data and "approval_status" in self.data:
                return self.data["approval_status"]

            need_item = nuci_config.find_child("uci.updater.approvals.need")
            if not need_item:
                return UpdaterAutoUpdatesHandler.APPROVAL_NO
            if not parse_uci_bool(need_item.value):
                return UpdaterAutoUpdatesHandler.APPROVAL_NO
            seconds_item = nuci_config.find_child(
                "uci.updater.approvals.auto_grant_seconds")
            if not seconds_item:
                return UpdaterAutoUpdatesHandler.APPROVAL_NEEDED

            try:
                hours = int(seconds_item.value)
            except ValueError:
                return UpdaterAutoUpdatesHandler.APPROVAL_NEEDED

            if hours < 0:
                return UpdaterAutoUpdatesHandler.APPROVAL_NEEDED

            return UpdaterAutoUpdatesHandler.APPROVAL_TIMEOUT

        form = fapi.ForisForm("updater_eula",
                              self.data,
                              filter=filters.create_config_filter(
                                  "updater", "foris"))
        main_section = form.add_section(name="agree_eula",
                                        title=_(self.userfriendly_title))
        main_section.add_field(
            Radio,
            name="agreed",
            label=_("I agree"),
            default="1",
            args=(("1", _("Use automatic updates (recommended)")),
                  ("0", _("Turn automatic updates off"))),
            nuci_preproc=lambda x: "1"
            if preproc_disabled_to_agreed(x) else "0")
        approval_section = form.add_section(name="approvals",
                                            title=_("Update approvals"))
        main_section.add_section(approval_section)
        approval_section.add_field(
            RadioSingle,
            name=UpdaterAutoUpdatesHandler.APPROVAL_NO,
            group="approval_status",
            label=_("Automatic installation"),
            hint=_("Updates will be installed without user's intervention."),
            nuci_preproc=lambda e: approval_preproc_approve_status(e),
        ).requires("agreed", "1")

        approval_section.add_field(
            RadioSingle,
            name=UpdaterAutoUpdatesHandler.APPROVAL_TIMEOUT,
            group="approval_status",
            label=_("Delayed updates"),
            hint=
            _("Updates will be installed with an adjustable delay. You can also approve them manually."
              ),
            nuci_preproc=lambda e: approval_preproc_approve_status(e),
        ).requires("agreed", "1")
        approval_section.add_field(
            Number,
            name="approval_timeout",
            nuci_path="uci.updater.approvals.auto_grant_seconds",
            nuci_preproc=lambda val: int(val.value) / 60 /
            60,  # seconds to hours
            validators=[validators.InRange(1, 24 * 7)],
            default=24,
            required=True,
            min=1,
            max=24 * 7,
        ).requires(UpdaterAutoUpdatesHandler.APPROVAL_TIMEOUT,
                   UpdaterAutoUpdatesHandler.APPROVAL_TIMEOUT).requires(
                       UpdaterAutoUpdatesHandler.APPROVAL_NO,
                       UpdaterAutoUpdatesHandler.APPROVAL_TIMEOUT).requires(
                           UpdaterAutoUpdatesHandler.APPROVAL_NEEDED,
                           UpdaterAutoUpdatesHandler.APPROVAL_TIMEOUT)

        approval_section.add_field(
            RadioSingle,
            name=UpdaterAutoUpdatesHandler.APPROVAL_NEEDED,
            group="approval_status",
            label=_("Update approval needed"),
            hint=
            _("You have to approve the updates, otherwise they won't be installed."
              ),
            nuci_preproc=lambda e: approval_preproc_approve_status(e),
        ).requires("agreed", "1")

        def form_cb(data):
            agreed = bool(int(data.get("agreed", "0")))
            approval_status = data.get(UpdaterAutoUpdatesHandler.APPROVAL_NO,
                                       UpdaterAutoUpdatesHandler.APPROVAL_NO)
            auto_grant_seconds = int(data.get("approval_timeout",
                                              24)) * 60 * 60

            uci = Uci()
            updater = uci.add(Config("updater"))
            override = updater.add(Section("override", "override"))
            override.add(Option("disable", not agreed))

            approvals = updater.add_replace(Section("approvals", "approvals"))
            if approval_status == UpdaterAutoUpdatesHandler.APPROVAL_NO:
                approvals.add(Option("need", "0"))
            elif approval_status == UpdaterAutoUpdatesHandler.APPROVAL_NEEDED:
                approvals.add(Option("need", "1"))
            elif approval_status == UpdaterAutoUpdatesHandler.APPROVAL_TIMEOUT:
                approvals.add(Option("need", "1"))
                approvals.add(Option("auto_grant_seconds", auto_grant_seconds))

            return "edit_config", uci

        def save_result_cb(data):
            return "save_result", {
                'agreed': bool(int(data.get("agreed", "0")))
            }

        form.add_callback(form_cb)
        form.add_callback(save_result_cb)
        return form