示例#1
0
class NetworksHandler(BaseConfigHandler):
    """ Networks settings handler
    """
    userfriendly_title = gettext("Network interfaces")

    def load_backend_data(self):
        data = current_state.backend.perform("networks", "get_settings")

        self.backend_data = data

    def __init__(self, *args, **kwargs):
        self.load_backend_data()
        super(NetworksHandler, self).__init__(*args, **kwargs)

    def get_form(self):
        data = copy.deepcopy(self.backend_data)

        if self.data:
            # Update from post
            data.update(self.data)

        networks_form = fapi.ForisForm("networks", self.data)
        ports_section = networks_form.add_section(
            name="set_ports", title=_(self.userfriendly_title))
        checkboxes = []
        for kind in ["wan", "lan", "guest", "none"]:
            checkboxes += [(e["id"], e["id"]) for e in self.backend_data["networks"][kind]]
        ports_section.add_field(MultiCheckbox, name="wan", args=checkboxes, multifield=True)
        ports_section.add_field(MultiCheckbox, name="lan", args=checkboxes, multifield=True)
        ports_section.add_field(MultiCheckbox, name="guest", args=checkboxes, multifield=True)
        ports_section.add_field(MultiCheckbox, name="none", args=checkboxes, multifield=True)

        ports_section.add_field(Checkbox, name="ssh_on_wan", default=False, required=False)
        ports_section.add_field(Checkbox, name="http_on_wan", default=False, required=False)
        ports_section.add_field(Checkbox, name="https_on_wan", default=False, required=False)

        def networks_form_cb(data):
            wan = data.get("wan", [])
            lan = data.get("lan", [])
            guest = data.get("guest", [])
            none = data.get("none", [])

            ssh_on_wan = bool(data.get("ssh_on_wan", "0"))
            http_on_wan = bool(data.get("http_on_wan", "0"))
            https_on_wan = bool(data.get("https_on_wan", "0"))

            result = current_state.backend.perform(
                "networks", "update_settings", {
                    "firewall": {
                        "ssh_on_wan": ssh_on_wan, "http_on_wan": http_on_wan,
                        "https_on_wan": https_on_wan
                    },
                    "networks": {"lan": lan, "wan": wan, "guest": guest, "none": none}
                }
            )
            return "save_result", result

        networks_form.add_callback(networks_form_cb)

        return networks_form
示例#2
0
class SamplePluginConfigHandler(BaseConfigHandler):
    # gettext() triggers lazy_translated text
    # it is also used for detecting translations during foris_make_messages cmd

    userfriendly_title = gettext("Sample")

    def get_form(self):
        data = current_state.backend.perform("sample", "get_slices")

        if self.data:
            # Update from post (used when the form is updated via ajax)
            data.update(self.data)

        form = fapi.ForisForm("sample", data)
        section = form.add_section(
            name="main_section",
            title=self.userfriendly_title,
        )
        # _() translates the string immediatelly
        # it is also used for detecting translations during foris_make_messages cmd
        section.add_field(Number,
                          name="slices",
                          label=_("Number of slices"),
                          required=True,
                          validators=validators.InRange(2, 15))

        def form_cb(data):
            res = current_state.backend.perform(
                "sample", "set_slices", {"slices": int(data["slices"])})

            return "save_result", res  # store {"result": ...} to be used in SamplePluginPage save() method

        form.add_callback(form_cb)
        return form
示例#3
0
class StoragePluginPage(ConfigPageMixin, StoragePluginConfigHandler):
    slug = "storage"
    menu_order = 60
    template = "storage/storage"
    template_type = "jinja2"
    userfriendly_title = gettext("Storage")

    def render(self, **kwargs):
        kwargs['settings'] = current_state.backend.perform("storage", "get_settings")
        kwargs['settings']['old_device_name'] = \
            kwargs['settings']["old_device"].replace("/dev/", "")

        available_modules = current_state.backend.perform("introspect", "list_modules")
        if "nextcloud" in available_modules["modules"]:
            nextcloud_data = current_state.backend.perform("nextcloud", "get_status")
            kwargs["settings"]["nextcloud_installed"] = nextcloud_data["nextcloud_installed"]
            kwargs["settings"]["nextcloud_configured"] = nextcloud_data["nextcloud_configured"]
            kwargs["settings"]["nextcloud_configuring"] = nextcloud_data["nextcloud_configuring"]

        drives = current_state.backend.perform("storage", "get_drives")["drives"]
        kwargs['drives'] = sorted(drives, key=lambda d: d['dev'])
        return super(StoragePluginPage, self).render(**kwargs)

    def call_ajax_action(self, action):
        if action == 'get_settings':
            if bottle.request.method != 'GET':
                raise bottle.HTTPError(404, "Wrong http method (only GET is allowed.")

            data = current_state.backend.perform("storage", "get_settings")
            return data

        raise ValueError("Unknown AJAX action.")
示例#4
0
class CollectionToggleHandler(BaseConfigHandler):
    userfriendly_title = gettext("Data collection")

    def get_form(self):
        data = current_state.backend.perform("data_collect", "get")
        if self.data and "enable" in self.data:
            data["enable"] = self.data["enable"]
        else:
            data["enable"] = data["agreed"]

        form = fapi.ForisForm("enable_collection", data)

        section = form.add_section(
            name="collection_toggle",
            title=_(self.userfriendly_title),
        )
        section.add_field(Checkbox,
                          name="enable",
                          label=_("Enable data collection"),
                          preproc=lambda val: bool(int(val)))

        def form_cb(data):
            data = current_state.backend.perform("data_collect", "set",
                                                 {"agreed": data["enable"]})
            return "save_result", data  # store {"result": ...} to be used later...

        form.add_callback(form_cb)

        return form
示例#5
0
文件: wifi.py 项目: CZ-NIC/foris
class WifiHandler(BaseConfigHandler):
    userfriendly_title = gettext("Wi-Fi")

    def get_form(self):
        ajax_form = WifiEditForm(self.data)

        return ajax_form.foris_form
示例#6
0
class SsbackupsPluginConfigHandler(BaseConfigHandler):
    userfriendly_title = gettext("Cloud Backups")

    def get_form(self):
        form = fapi.ForisForm("create_and_upload", self.data)
        section = form.add_section(
            name="passwords",
            title=self.userfriendly_title,
        )
        section.add_field(
            Password, name="password", label=_("Password"), required=True,
            validators=validators.LenRange(6, 128)
        )
        section.add_field(
            Password, name="password_validation", label=_("Password (repeat)"), required=True,
            validators=validators.EqualTo(
                "password", "password_validation", _("Passwords are not equal.")
            )
        )

        def form_cb(data):
            res = current_state.backend.perform(
                "ssbackups", "set_password",
                {"password": base64.b64encode(data["password"].encode()).decode()}
            )
            return "save_result", res  # store {"result": ...} to be used later...

        form.add_callback(form_cb)
        return form
示例#7
0
文件: profile.py 项目: erdoukki/foris
class ProfileHandler(BaseConfigHandler):
    """ Profile settings handler
    """
    userfriendly_title = gettext("Guide workflow")

    def __init__(self, *args, **kwargs):
        self.load_backend_data()
        super(ProfileHandler, self).__init__(*args, **kwargs)

    def load_backend_data(self):
        self.backend_data = current_state.backend.perform("web", "get_guide")

    def get_form(self):

        data = {"workflow": self.backend_data["current_workflow"]}
        if self.data:
            data.update(self.data)

        profile_form = fapi.ForisForm("profile", data)
        main = profile_form.add_section(name="set_profile", title=_(self.userfriendly_title))
        main.add_field(Hidden, name="workflow", value=self.backend_data["current_workflow"])

        def profile_form_cb(data):
            result = current_state.backend.perform(
                "web", "update_guide", {
                    "enabled": True,
                    "workflow": data["workflow"],
                }
            )
            return "save_result", result

        profile_form.add_callback(profile_form_cb)

        return profile_form
示例#8
0
class StoragePluginConfigHandler(BaseConfigHandler):
    userfriendly_title = gettext("storage")

    def get_form(self):
        form = fapi.ForisForm("storage", [])
        form.add_section(name="set_srv", title=self.userfriendly_title)

        return form
示例#9
0
class NotificationsConfigPage(ConfigPageMixin):
    slug = "notifications"

    menu_order = 9

    template = "config/notifications"
    userfriendly_title = gettext("Notifications")
    template_type = "jinja2"

    def render(self, **kwargs):
        notifications = current_state.backend.perform(
            "router_notifications", "list",
            {"lang": current_state.language})["notifications"]

        # show only non displayed notifications
        kwargs["notifications"] = [
            e for e in notifications if not e["displayed"]
        ]

        return super(NotificationsConfigPage, self).render(**kwargs)

    def _action_dismiss_notifications(self):
        notification_ids = bottle.request.POST.getall("notification_ids[]")
        response = current_state.backend.perform("router_notifications",
                                                 "mark_as_displayed",
                                                 {"ids": notification_ids})
        return response["result"], notification_ids

    def call_ajax_action(self, action):
        if action == "dismiss-notifications":
            bottle.response.set_header("Content-Type", "application/json")
            res = self._action_dismiss_notifications()
            if res[0]:
                return {"success": True, "displayedIDs": res[1]}
            else:
                return {"success": False}

        elif action == "list":
            notifications = current_state.backend.perform(
                "router_notifications", "list",
                {"lang": current_state.language})["notifications"]
            return bottle.template(
                "_notifications.html.j2",
                notifications=[e for e in notifications if not e["displayed"]],
                template_adapter=bottle.Jinja2Template,
            )

        raise ValueError("Unknown AJAX action.")

    @classmethod
    def get_menu_tag(cls):
        return {
            "show": True if current_state.notification_count else False,
            "hint": _("Number of notifications"),
            "text": "%d" % current_state.notification_count,
        }
示例#10
0
class AboutConfigPage(ConfigPageMixin):
    slug = "about"
    menu_order = 99

    template = "config/about"
    template_type = "jinja2"
    userfriendly_title = gettext("About")

    def render(self, **kwargs):
        data = current_state.backend.perform("about", "get")
        # process dates etc
        return self.default_template(data=data, **kwargs)
示例#11
0
class NetmetrPluginConfigHandler(BaseConfigHandler):
    userfriendly_title = gettext("netmetr")

    def get_form(self):
        data = current_state.backend.perform("netmetr", "get_settings")
        # init hours
        for i in range(24):
            data["hour_to_run_%d" % i] = False
        # update the enabled
        for e in data["hours_to_run"]:
            data["hour_to_run_%d" % e] = True

        if self.data:
            # Update from post
            data.update(self.data)

        form = fapi.ForisForm("netmetr", data)
        main = form.add_section(name="set_netmetr",
                                title=_(self.userfriendly_title))
        autostart_section = main.add_section(name="autostart",
                                             title=_("Autostart"))
        autostart_section.add_field(
            Checkbox,
            name="autostart_enabled",
            label=_("Autostart enabled"),
            preproc=lambda val: bool(int(val)),
            hint=
            _("Measuring will start about selected hour (time is no exact for load distribution)"
              ))
        hours = main.add_section(name="hours", title=_("Autostart times"))
        for i in range(24):
            hours.add_field(
                Checkbox,
                name="hour_to_run_%d" % i,
                label="%02d:00" % i,
                preproc=lambda val: bool(int(val)),
            ).requires("autostart_enabled", True)

        def form_cb(data):
            msg = {"autostart_enabled": data["autostart_enabled"]}
            if data["autostart_enabled"]:
                msg["hours_to_run"] = []
                for i in range(24):
                    name = "hour_to_run_%d" % i
                    if name in data and data[name]:
                        msg["hours_to_run"].append(i)
            current_state.backend.perform("netmetr", "update_settings", msg)
            messages.success(_("Netmetr settings were updated."))
            return "none", None

        form.add_callback(form_cb)

        return form
示例#12
0
文件: misc.py 项目: CZ-NIC/foris
class GuideFinishedHandler(BaseConfigHandler):
    userfriendly_title = gettext("Guide Finished")

    def get_form(self):
        finished_form = fapi.ForisForm("guide_finished", {})
        finished_form.add_section(name="guide_finished",
                                  title=_(self.userfriendly_title))

        def guide_finished_cb(data):
            res = current_state.backend.perform("web", "update_guide",
                                                {"enabled": False})
            return "save_result", res  # store {"result": ...} to be used later...

        finished_form.add_callback(guide_finished_cb)
        return finished_form
示例#13
0
class NetbootConfigHandler(BaseConfigHandler):
    STATE_ACCEPTED = gettext("accepted")
    STATE_INCOMMING = gettext("incomming")

    userfriendly_title = gettext("Netboot")

    def get_form(self):

        form = fapi.ForisForm("netboot", {})
        form.add_section(name="main_section", title=self.userfriendly_title)

        def form_cb(data):
            return "save_result", {}

        form.add_callback(form_cb)
        return form

    def get_serial_form(self, data=None):
        generate_serial_form = fapi.ForisForm("serial_form", data)
        serial_section = generate_serial_form.add_section("serial_section", title=None)
        serial_section.add_field(
            Textbox, name="serial", label=" ", required=True, validators=[validators.MacAddress()]
        )
        return generate_serial_form
示例#14
0
class MaintenanceHandler(BaseConfigHandler):
    userfriendly_title = gettext("Maintenance")

    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):
            data = current_state.backend.perform(
                "maintain",
                "restore_backup",
                {"backup": base64.b64encode(data["backup_file"].file.read()).decode("utf-8")},
            )
            return "save_result", {"result": data["result"]}

        maintenance_form.add_callback(maintenance_form_cb)
        return maintenance_form
示例#15
0
class GuideFinishedHandler(BaseConfigHandler):
    userfriendly_title = gettext("Guide Finished")

    def get_form(self):
        finished_form = fapi.ForisForm("guide_finished", {})
        finished_form.add_section(
            name="guide_finished",
            title=_(self.userfriendly_title),
            description=_(
                "Congratulations you've successfully reached the end of this guide. "
                "Once you leave this guide you'll be granted access to the "
                "full configuration interface of this device."))

        def guide_finished_cb(data):
            res = current_state.backend.perform("web", "update_guide", {
                "enabled": False,
            })
            return "save_result", res  # store {"result": ...} to be used later...

        finished_form.add_callback(guide_finished_cb)
        return finished_form
示例#16
0
class RegistrationCheckHandler(BaseConfigHandler):
    """
    Handler for checking of the registration status and assignment to a queried email address.
    """

    userfriendly_title = gettext("Data collection")

    def get_form(self):
        form = fapi.ForisForm("registration_check", self.data)
        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):
            data = current_state.backend.perform(
                "data_collect", "get_registered", {
                    "email": data.get("email"),
                    "language": current_state.language
                })
            error = None
            registration_number = None
            url = None
            if data["status"] == "unknown":
                error = _("Failed to query the server.")
            elif data["status"] == "not_valid":
                error = _("Failed to verify the router's registration.")
            elif data["status"] in ["free", "foreign"]:
                url = data["url"]
                registration_number = data["registration_number"]

            return "save_result", {
                'success': data["status"] not in ["unknown", "not_valid"],
                'status': data["status"],
                'error': error,
                'url': url,
                'registration_number': registration_number,
            }

        form.add_callback(form_cb)
        return form
class SubordinatesJoinedPage(JoinedPages):
    userfriendly_title = gettext("Managed devices")
    slug = "subordinates"
    no_url = True

    subpages: typing.Iterable[typing.Type["ConfigPageMixin"]] = [
        SubordinatesSetupPage,
        SubordinatesWifiPage,
        SubordinatesNetbootPage,
    ]

    @classmethod
    def is_visible(cls):
        if current_state.backend.name != "mqtt":
            return False
        return ConfigPageMixin.is_visible_static(cls)

    @classmethod
    def is_enabled(cls):
        if current_state.backend.name != "mqtt":
            return False
        return ConfigPageMixin.is_enabled_static(cls)
示例#18
0
class TLSConfigHandler(BaseConfigHandler):
    userfriendly_title = gettext("Access tokens")

    def get_form(self):
        tls_form = ForisForm("tls", self.data, filter=ca_filter)
        maintenance_main = tls_form.add_section(name="tls_client",
                                                title=_(
                                                    self.userfriendly_title))
        maintenance_main.add_field(
            Textbox,
            name="client_name",
            label=_("Client name"),
            required=True,
            hint=
            _("The display name for the client. It must be shorter than 64 characters "
              "and must contain only alphanumeric characters, dots, dashes and "
              "underscores."),
            validators=[
                RegExp(_("Client name is invalid."), client_name_regexp),
                LenRange(1, 63)
            ])

        def maintenance_form_cb(data):
            client_name = data['client_name']
            if new_client(client_name):
                messages.success(
                    _("Request for creating a new client \"%s\" was succesfully submitted. "
                      "Client token should be available for download in a minute."
                      ) % client_name)
            else:
                messages.error(
                    _("An error occurred when creating client \"%s\".") %
                    client_name)
            return "none", None

        tls_form.add_callback(maintenance_form_cb)
        return tls_form
示例#19
0
文件: wan.py 项目: CZ-NIC/foris
class WanHandler(BaseConfigHandler):
    userfriendly_title = gettext("WAN")

    def __init__(self, *args, **kwargs):
        # Do not display "none" options for WAN protocol if hide_no_wan is True
        self.hide_no_wan = kwargs.pop("hide_no_wan", False)
        self.status_data = current_state.backend.perform(
            "wan", "get_wan_status")
        self.backend_data = current_state.backend.perform(
            "wan", "get_settings")
        super(WanHandler, self).__init__(*args, **kwargs)

    @staticmethod
    def _convert_backend_data_to_form_data(data):
        res = {}

        # WAN
        # Convert none (initial setup) to dhcp (default)
        res["proto"] = ("dhcp" if data["wan_settings"]["wan_type"] == "none"
                        else data["wan_settings"]["wan_type"])
        if res["proto"] == "dhcp":
            res["hostname"] = data["wan_settings"].get("wan_dhcp",
                                                       {}).get("hostname", "")
        elif res["proto"] == "static":
            res["ipaddr"] = data["wan_settings"]["wan_static"]["ip"]
            res["netmask"] = data["wan_settings"]["wan_static"]["netmask"]
            res["gateway"] = data["wan_settings"]["wan_static"]["gateway"]
            res["ipv4_dns1"] = data["wan_settings"]["wan_static"].get(
                "dns1", "")
            res["ipv4_dns2"] = data["wan_settings"]["wan_static"].get(
                "dns2", "")
        elif res["proto"] == "pppoe":
            res["username"] = data["wan_settings"]["wan_pppoe"]["username"]
            res["password"] = data["wan_settings"]["wan_pppoe"]["password"]

        # WAN6
        res["wan6_proto"] = data["wan6_settings"]["wan6_type"]
        if res["wan6_proto"] == "static":
            res["ip6addr"] = data["wan6_settings"]["wan6_static"]["ip"]
            res["ip6prefix"] = data["wan6_settings"]["wan6_static"]["network"]
            res["ip6gw"] = data["wan6_settings"]["wan6_static"]["gateway"]
            res["ipv6_dns1"] = data["wan6_settings"]["wan6_static"].get(
                "dns1", "")
            res["ipv6_dns2"] = data["wan6_settings"]["wan6_static"].get(
                "dns2", "")
        elif res["wan6_proto"] == "dhcpv6":
            res["ip6duid"] = data["wan6_settings"]["wan6_dhcpv6"]["duid"]
        elif res["wan6_proto"] == "6to4":
            res["6to4_ipaddr"] = data["wan6_settings"]["wan6_6to4"][
                "ipv4_address"]
        elif res["wan6_proto"] == "6in4":
            res["6in4_mtu"] = data["wan6_settings"]["wan6_6in4"]["mtu"]
            res["6in4_server_ipv4"] = data["wan6_settings"]["wan6_6in4"][
                "server_ipv4"]
            res["6in4_ipv6_prefix"] = data["wan6_settings"]["wan6_6in4"][
                "ipv6_prefix"]
            res["6in4_dynamic_enabled"] = data["wan6_settings"]["wan6_6in4"][
                "dynamic_ipv4"]["enabled"]
            if res["6in4_dynamic_enabled"]:
                res["6in4_tunnel_id"] = data["wan6_settings"]["wan6_6in4"][
                    "dynamic_ipv4"]["tunnel_id"]
                res["6in4_username"] = data["wan6_settings"]["wan6_6in4"][
                    "dynamic_ipv4"]["username"]
                res["6in4_key"] = data["wan6_settings"]["wan6_6in4"][
                    "dynamic_ipv4"]["password_or_key"]

        # MAC
        res["custom_mac"] = data["mac_settings"]["custom_mac_enabled"]
        res["macaddr"] = data["mac_settings"].get("custom_mac", "")

        return res

    @staticmethod
    def _convert_form_data_to_backend_data(data):
        res = {"wan_settings": {}, "wan6_settings": {}, "mac_settings": {}}

        # WAN
        res["wan_settings"]["wan_type"] = data["proto"]
        if data["proto"] == "dhcp":
            hostname = data.get("hostname", False)
            res["wan_settings"]["wan_dhcp"] = {
                "hostname": hostname
            } if hostname else {}
        elif data["proto"] == "static":
            res["wan_settings"]["wan_static"] = {}
            res["wan_settings"]["wan_static"]["ip"] = data["ipaddr"]
            res["wan_settings"]["wan_static"]["netmask"] = data["netmask"]
            res["wan_settings"]["wan_static"]["gateway"] = data["gateway"]
            dns1 = data.get("ipv4_dns1", None)
            dns2 = data.get("ipv4_dns2", None)
            res["wan_settings"]["wan_static"].update(
                {k: v
                 for k, v in {
                     "dns1": dns1,
                     "dns2": dns2
                 }.items() if v})
        elif data["proto"] == "pppoe":
            res["wan_settings"]["wan_pppoe"] = {}
            res["wan_settings"]["wan_pppoe"]["username"] = data["username"]
            res["wan_settings"]["wan_pppoe"]["password"] = data["password"]

        # WAN6
        res["wan6_settings"]["wan6_type"] = data["wan6_proto"]
        if data["wan6_proto"] == "static":
            res["wan6_settings"]["wan6_static"] = {}
            res["wan6_settings"]["wan6_static"]["ip"] = data["ip6addr"]
            res["wan6_settings"]["wan6_static"]["network"] = data["ip6prefix"]
            res["wan6_settings"]["wan6_static"]["gateway"] = data["ip6gw"]
            dns1 = data.get("ipv6_dns1", None)
            dns2 = data.get("ipv6_dns2", None)
            res["wan6_settings"]["wan6_static"].update(
                {k: v
                 for k, v in {
                     "dns1": dns1,
                     "dns2": dns2
                 }.items() if v})
        if data["wan6_proto"] == "dhcpv6":
            res["wan6_settings"]["wan6_dhcpv6"] = {
                "duid": data.get("ip6duid", "")
            }
        if data["wan6_proto"] == "6to4":
            res["wan6_settings"]["wan6_6to4"] = {
                "ipv4_address": data.get("6to4_ipaddr", "")
            }
        if data["wan6_proto"] == "6in4":
            dynamic = {"enabled": data.get("6in4_dynamic_enabled", False)}
            if dynamic["enabled"]:
                dynamic["tunnel_id"] = data.get("6in4_tunnel_id")
                dynamic["username"] = data.get("6in4_username")
                dynamic["password_or_key"] = data.get("6in4_key")
            res["wan6_settings"]["wan6_6in4"] = {
                "mtu": int(data.get("6in4_mtu")),
                "ipv6_prefix": data.get("6in4_ipv6_prefix"),
                "server_ipv4": data.get("6in4_server_ipv4"),
                "dynamic_ipv4": dynamic,
            }

        # MAC
        res["mac_settings"] = ({
            "custom_mac_enabled": True,
            "custom_mac": data["macaddr"]
        } if "custom_mac" in data and data["custom_mac"] else {
            "custom_mac_enabled": False
        })

        return res

    def get_form(self):
        data = WanHandler._convert_backend_data_to_form_data(self.backend_data)

        if self.data:
            # Update from post
            data.update(self.data)

        # WAN
        wan_form = fapi.ForisForm("wan", data)
        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 these "
              "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_6TO4 = "6to4"
        WAN6_6IN4 = "6in4"

        WAN6_OPTIONS = (
            (WAN6_DHCP, _("DHCPv6 (automatic configuration)")),
            (WAN6_STATIC, _("Static IP address (manual configuration)")),
            (WAN6_6TO4, _("6to4 (public IPv4 address required)")),
            (WAN6_6IN4, _("6in4 (public IPv4 address required)")),
        )

        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"),
                           args=WAN_OPTIONS,
                           default=WAN_DHCP)

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

        wan_main.add_field(
            Textbox,
            name="hostname",
            label=_("DHCP hostname"),
            validators=validators.Domain(),
            hint=_("Hostname which will be provided to DHCP server."),
        ).requires("proto", WAN_DHCP)

        # DNS servers
        wan_main.add_field(
            Textbox,
            name="ipv4_dns1",
            label=_("DNS server 1 (IPv4)"),
            validators=validators.IPv4(),
            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="ipv4_dns2",
            label=_("DNS server 2 (IPv4)"),
            validators=validators.IPv4(),
            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"),
            required=True,
        ).requires("proto", WAN_PPPOE)
        wan_main.add_field(
            PasswordWithHide,
            name="password",
            label=_("PAP/CHAP password"),
            required=True,
        ).requires("proto", WAN_PPPOE)

        # IPv6 configuration
        wan_main.add_field(
            Dropdown,
            name="wan6_proto",
            label=_("IPv6 protocol"),
            args=WAN6_OPTIONS,
            default=WAN6_NONE,
        )
        wan_main.add_field(
            Textbox,
            name="ip6addr",
            label=_("IPv6 address"),
            validators=validators.IPv6Prefix(),
            required=True,
            hint=_("IPv6 address and prefix length for WAN interface, "
                   "e.g. 2001:db8:be13:37da::1/64"),
        ).requires("wan6_proto", WAN6_STATIC)
        wan_main.add_field(
            Textbox,
            name="ip6gw",
            label=_("IPv6 gateway"),
            validators=validators.IPv6(),
            required=True,
        ).requires("wan6_proto", WAN6_STATIC)
        wan_main.add_field(
            Textbox,
            name="ip6prefix",
            label=_("IPv6 prefix"),
            validators=validators.IPv6Prefix(),
            hint=_("Address range for local network, "
                   "e.g. 2001:db8:be13:37da::/64"),
        ).requires("wan6_proto", WAN6_STATIC)
        # DNS servers
        wan_main.add_field(
            Textbox,
            name="ipv6_dns1",
            label=_("DNS server 1 (IPv6)"),
            validators=validators.IPv6(),
            hint=_("DNS server address is not required as the built-in "
                   "DNS resolver is capable of working without it."),
        ).requires("wan6_proto", WAN6_STATIC)
        wan_main.add_field(
            Textbox,
            name="ipv6_dns2",
            label=_("DNS server 2 (IPv6)"),
            validators=validators.IPv6(),
            hint=_("DNS server address is not required as the built-in "
                   "DNS resolver is capable of working without it."),
        ).requires("wan6_proto", WAN6_STATIC)
        wan_main.add_field(
            Textbox,
            name="ip6duid",
            label=_("Custom DUID"),
            validators=validators.Duid(),
            placeholder=self.status_data["last_seen_duid"],
            hint=_("DUID which will be provided to the DHCPv6 server."),
        ).requires("wan6_proto", WAN6_DHCP)
        wan_main.add_field(
            Textbox,
            name="6to4_ipaddr",
            label=_("Public IPv4"),
            validators=validators.IPv4(),
            hint=
            _("In order to use 6to4 protocol, you might need to specify your public IPv4 "
              "address manually (e.g. when your WAN interface has a private address which "
              "is mapped to public IP)."),
            placeholder=_("use autodetection"),
            required=False,
        ).requires("wan6_proto", WAN6_6TO4)
        wan_main.add_field(
            Textbox,
            name="6in4_server_ipv4",
            label=_("Provider IPv4"),
            validators=validators.IPv4(),
            hint=
            _("This address will be used as a endpoint of the tunnel on the provider's side."
              ),
            required=True,
        ).requires("wan6_proto", WAN6_6IN4)
        wan_main.add_field(
            Textbox,
            name="6in4_ipv6_prefix",
            label=_("Routed IPv6 prefix"),
            validators=validators.IPv6Prefix(),
            hint=_("IPv6 addresses which will be routed to your network."),
            required=True,
        ).requires("wan6_proto", WAN6_6IN4)
        wan_main.add_field(
            Number,
            name="6in4_mtu",
            label=_("MTU"),
            validators=validators.InRange(1280, 1500),
            hint=_("Maximum Transmission Unit in the tunnel (in bytes)."),
            required=True,
            default="1480",
        ).requires("wan6_proto", WAN6_6IN4)
        wan_main.add_field(
            Checkbox,
            name="6in4_dynamic_enabled",
            label=_("Dynamic IPv4 handling"),
            hint=_(
                "Some tunnel providers allow you to have public dynamic IPv4. "
                "Note that you need to fill in some extra fields to make it work."
            ),
            default=False,
        ).requires("wan6_proto", WAN6_6IN4)
        wan_main.add_field(
            Textbox,
            name="6in4_tunnel_id",
            label=_("Tunnel ID"),
            validators=validators.NotEmpty(),
            hint=_(
                "ID of your tunnel which was assigned to you by the provider."
            ),
            required=True,
        ).requires("6in4_dynamic_enabled", True)
        wan_main.add_field(
            Textbox,
            name="6in4_username",
            label=_("Username"),
            validators=validators.NotEmpty(),
            hint=
            _("Username which will be used to provide credentials to your tunnel provider."
              ),
            required=True,
        ).requires("6in4_dynamic_enabled", True)
        wan_main.add_field(
            Textbox,
            name="6in4_key",
            label=_("Key"),
            validators=validators.NotEmpty(),
            hint=
            _("Key which will be used to provide credentials to your tunnel provider."
              ),
            required=True,
        ).requires("6in4_dynamic_enabled", True)

        # custom MAC
        wan_main.add_field(
            Checkbox,
            name="custom_mac",
            label=_("Custom MAC address"),
            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"),
            validators=validators.MacAddress(),
            required=True,
            hint=_(
                "Colon is used as a separator, for example 00:11:22:33:44:55"),
        ).requires("custom_mac", True)

        def wan_form_cb(data):
            backend_data = WanHandler._convert_form_data_to_backend_data(data)
            res = current_state.backend.perform("wan", "update_settings",
                                                backend_data)

            return "save_result", res  # store {"result": ...} to be used later...

        wan_form.add_callback(wan_form_cb)

        return wan_form
示例#20
0
文件: guest.py 项目: CZ-NIC/foris
    def get_form(self):
        data = {}
        data["guest_enabled"] = self.backend_data["enabled"]
        data["guest_ipaddr"] = self.backend_data["ip"]
        data["guest_netmask"] = self.backend_data["netmask"]
        data["guest_dhcp_enabled"] = self.backend_data["dhcp"]["enabled"]
        data["guest_dhcp_start"] = self.backend_data["dhcp"]["start"]
        data["guest_dhcp_limit"] = self.backend_data["dhcp"]["limit"]
        data["guest_dhcp_leasetime"] = self.backend_data["dhcp"]["lease_time"] // 60 // 60
        data["guest_qos_enabled"] = self.backend_data["qos"]["enabled"]
        data["guest_qos_download"] = self.backend_data["qos"]["download"]
        data["guest_qos_upload"] = self.backend_data["qos"]["upload"]

        if self.data:
            # Update from post
            data.update(self.data)

        guest_form = fapi.ForisForm(
            "guest",
            data,
            validators=[
                validators.DhcpRangeValidator(
                    "guest_netmask",
                    "guest_dhcp_start",
                    "guest_dhcp_limit",
                    gettext(
                        "<strong>DHCP start</strong> and <strong>DHCP max leases</strong> "
                        "does not fit into <strong>Guest network netmask</strong>!"
                    ),
                    [
                        lambda data: not data.get("guest_enabled"),
                        lambda data: not data.get("guest_dhcp_enabled"),
                    ],
                ),
                validators.DhcpRangeRouterIpValidator(
                    "guest_ipaddr",
                    "guest_netmask",
                    "guest_dhcp_start",
                    "guest_dhcp_limit",
                    gettext(
                        "<strong>Router IP</strong> should not be within DHCP range "
                        "defined by <strong>DHCP start</strong> and <strong>DHCP max leases "
                        "</strong>"
                    ),
                    [
                        lambda data: not data.get("guest_dhcp_enabled"),
                        lambda data: not data.get("guest_enabled"),
                    ],
                ),
            ],
        )
        guest_network_section = guest_form.add_section(
            name="guest_network",
            title=_(self.userfriendly_title),
            description=_(
                "Guest network is used for <a href='%(url)s'>guest Wi-Fi</a>. It is separated  "
                "from your ordinary LAN. Devices connected to this network are allowed "
                "to access the internet, but are not allowed to access the configuration "
                "interface of this device nor the devices in LAN."
            )
            % dict(url=reverse("config_page", page_name="wifi")),
        )
        guest_network_section.add_field(
            Checkbox, name="guest_enabled", label=_("Enable guest network"), default=False
        )
        guest_network_section.add_field(
            Textbox,
            name="guest_ipaddr",
            label=_("Router IP in guest network"),
            default=DEFAULT_GUEST_IP,
            validators=validators.IPv4(),
            hint=_(
                "Router's IP address in the guest network. It is necessary that "
                "the guest network IPs are different from other networks "
                "(LAN, WAN, VPN, etc.)."
            ),
        ).requires("guest_enabled", True)
        guest_network_section.add_field(
            Textbox,
            name="guest_netmask",
            label=_("Guest network netmask"),
            default=DEFAULT_GUEST_MASK,
            validators=validators.IPv4Netmask(),
            hint=_("Network mask of the guest network."),
        ).requires("guest_enabled", True)

        guest_network_section.add_field(
            Checkbox,
            name="guest_dhcp_enabled",
            label=_("Enable DHCP"),
            preproc=lambda val: bool(int(val)),
            default=True,
            hint=_(
                "Enable this option to automatically assign IP addresses to "
                "the devices connected to the router."
            ),
        ).requires("guest_enabled", True)
        guest_network_section.add_field(
            Textbox, name="guest_dhcp_start", label=_("DHCP start")
        ).requires("guest_dhcp_enabled", True)
        guest_network_section.add_field(
            Textbox, name="guest_dhcp_limit", label=_("DHCP max leases")
        ).requires("guest_dhcp_enabled", True)
        guest_network_section.add_field(
            Textbox,
            name="guest_dhcp_leasetime",
            label=_("Lease time (hours)"),
            validators=[validators.InRange(1, 7 * 24)],
        ).requires("guest_dhcp_enabled", True)

        guest_network_section.add_field(
            Checkbox,
            name="guest_qos_enabled",
            label=_("Guest Lan QoS"),
            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_enabled", True)

        guest_network_section.add_field(
            Number,
            name="guest_qos_download",
            label=_("Download (kb/s)"),
            validators=[validators.PositiveInteger()],
            hint=_("Download speed in guest network (in kilobits per second)."),
            default=1024,
        ).requires("guest_qos_enabled", True)
        guest_network_section.add_field(
            Number,
            name="guest_qos_upload",
            label=_("Upload (kb/s)"),
            validators=[validators.PositiveInteger()],
            hint=_("Upload speed in guest network (in kilobits per second)."),
            default=1024,
        ).requires("guest_qos_enabled", True)

        def guest_form_cb(data):
            if data["guest_enabled"]:
                msg = {
                    "enabled": data["guest_enabled"],
                    "ip": data["guest_ipaddr"],
                    "netmask": data["guest_netmask"],
                    "dhcp": {"enabled": data["guest_dhcp_enabled"]},
                    "qos": {"enabled": data["guest_qos_enabled"]},
                }
                if data["guest_dhcp_enabled"]:
                    msg["dhcp"]["start"] = int(data["guest_dhcp_start"])
                    msg["dhcp"]["limit"] = int(data["guest_dhcp_limit"])
                    msg["dhcp"]["lease_time"] = int(data["guest_dhcp_leasetime"]) * 60 * 60

                if data["guest_qos_enabled"]:
                    msg["qos"]["download"] = int(data["guest_qos_download"])
                    msg["qos"]["upload"] = int(data["guest_qos_upload"])
            else:
                msg = {"enabled": False}

            res = current_state.backend.perform("guest", "update_settings", msg)
            return "save_result", res  # store {"result": ...} to be used later...

        guest_form.add_callback(guest_form_cb)

        return guest_form
示例#21
0
    def get_form(self):
        data = {}
        data["mode"] = self.backend_data["mode"]
        data["router_ip"] = self.backend_data["mode_managed"]["router_ip"]
        data["router_netmask"] = self.backend_data["mode_managed"]["netmask"]
        data["router_dhcp_enabled"] = self.backend_data["mode_managed"][
            "dhcp"]["enabled"]
        data["router_dhcp_start"] = self.backend_data["mode_managed"]["dhcp"][
            "start"]
        data["router_dhcp_limit"] = self.backend_data["mode_managed"]["dhcp"][
            "limit"]
        data["router_dhcp_leasetime"] = self.backend_data["mode_managed"]["dhcp"]["lease_time"] \
            // (60 * 60)
        data["client_proto_4"] = self.backend_data["mode_unmanaged"][
            "lan_type"]
        data["client_ip_4"] = self.backend_data["mode_unmanaged"][
            "lan_static"]["ip"]
        data["client_netmask_4"] = self.backend_data["mode_unmanaged"][
            "lan_static"]["netmask"]
        data["client_gateway_4"] = self.backend_data["mode_unmanaged"][
            "lan_static"]["gateway"]
        dns1 = self.backend_data["mode_unmanaged"]["lan_static"].get("dns1")
        if dns1:
            data["client_dns1_4"] = dns1
        dns2 = self.backend_data["mode_unmanaged"]["lan_static"].get("dns2")
        if dns2:
            data["client_dns2_4"] = dns2
        data["client_hostname_4"] = self.backend_data["mode_unmanaged"][
            "lan_dhcp"].get("hostname", "")

        if self.data:
            # Update from post
            data.update(self.data)

        lan_form = fapi.ForisForm(
            "lan",
            data,
            validators=[
                validators.DhcpRangeValidator(
                    'router_netmask', 'router_dhcp_start', 'router_dhcp_limit',
                    gettext(
                        "<strong>DHCP start</strong> and <strong>DHCP max leases</strong> "
                        "does not fit into <strong>Network netmask</strong>!"),
                    [
                        lambda data: data['mode'] != 'managed',
                        lambda data: not data['router_dhcp_enabled'],
                    ])
            ])
        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(
            Dropdown,
            name="mode",
            label=_("LAN mode"),
            args=[
                ("managed", _("Router")),
                ("unmanaged", _("Computer")),
            ],
            hint=
            _("Router mode means that this devices manages the LAN "
              "(acts as a router, can assing IP addresses, ...). "
              "Computer mode means that this device acts as a client in this network. "
              "It acts in a similar way as WAN, but it has opened ports for configuration "
              "interface and other services. "),
            default="managed",
        )

        # managed options
        lan_main.add_field(
            Textbox,
            name="router_ip",
            label=_("Router IP address"),
            validators=validators.IPv4(),
            hint=_("Router's IP address in the inner network.")).requires(
                "mode", "managed")
        lan_main.add_field(
            Textbox,
            name="router_netmask",
            label=_("Network netmask"),
            validators=validators.IPv4Netmask(),
            hint=_("Network mask of the inner network.")).requires(
                "mode", "managed")
        lan_main.add_field(
            Checkbox,
            name="router_dhcp_enabled",
            label=_("Enable DHCP"),
            preproc=lambda val: bool(int(val)),
            default=True,
            hint=_(
                "Enable this option to automatically assign IP addresses to "
                "the devices connected to the router.")).requires(
                    "mode", "managed")
        lan_main.add_field(
            Number,
            name="router_dhcp_start",
            label=_("DHCP start"),
        ).requires("router_dhcp_enabled", True)
        lan_main.add_field(
            Number,
            name="router_dhcp_limit",
            label=_("DHCP max leases"),
        ).requires("router_dhcp_enabled", True)
        lan_main.add_field(Number,
                           name="router_dhcp_leasetime",
                           label=_("Lease time (hours)"),
                           validators=[validators.InRange(1, 7 * 24)
                                       ]).requires("router_dhcp_enabled", True)

        # unmanaged options
        LAN_DHCP = "dhcp"
        LAN_STATIC = "static"
        LAN_NONE = "none"
        LAN_OPTIONS = (
            (LAN_DHCP, _("DHCP (automatic configuration)")),
            (LAN_STATIC, _("Static IP address (manual configuration)")),
            (LAN_NONE, _("Don't connect this device to LAN")),
        )
        lan_main.add_field(Dropdown,
                           name="client_proto_4",
                           label=_("IPv4 protocol"),
                           args=LAN_OPTIONS,
                           default=LAN_DHCP).requires("mode", "unmanaged")
        # unmanaged static
        lan_main.add_field(Textbox,
                           name="client_ip_4",
                           label=_("IPv4 address"),
                           required=True,
                           validators=validators.IPv4()).requires(
                               "client_proto_4", LAN_STATIC)
        lan_main.add_field(Textbox,
                           name="client_netmask_4",
                           label=_("Network mask"),
                           required=True,
                           validators=validators.IPv4Netmask()).requires(
                               "client_proto_4", LAN_STATIC)
        lan_main.add_field(
            Textbox,
            name="client_gateway_4",
            label=_("Gateway"),
            required=True,
            validators=validators.IPv4(),
        ).requires("client_proto_4", LAN_STATIC)
        lan_main.add_field(
            Textbox,
            name="client_dns1_4",
            label=_("DNS server 1 (IPv4)"),
            validators=validators.IPv4(),
            hint=_("DNS server address is not required as the built-in "
                   "DNS resolver is capable of working without it.")).requires(
                       "client_proto_4", LAN_STATIC)
        lan_main.add_field(
            Textbox,
            name="client_dns2_4",
            label=_("DNS server 2 (IPv4)"),
            validators=validators.IPv4(),
            hint=_("DNS server address is not required as the built-in "
                   "DNS resolver is capable of working without it.")).requires(
                       "client_proto_4", LAN_STATIC)
        # unamanaged dhcp
        lan_main.add_field(
            Textbox,
            name="client_hostname_4",
            label=_("DHCP hostname"),
            validators=validators.Domain(),
            hint=_(
                "Hostname which will be provided to DHCP server.")).requires(
                    "client_proto_4", LAN_DHCP)

        def lan_form_cb(data):
            msg = {"mode": data["mode"]}
            if msg["mode"] == "managed":
                dhcp = {
                    "enabled": data["router_dhcp_enabled"],
                }
                if dhcp["enabled"]:
                    dhcp["start"] = int(data["router_dhcp_start"])
                    dhcp["limit"] = int(data["router_dhcp_limit"])
                    dhcp["lease_time"] = int(
                        data.get("router_dhcp_leasetime", 12)) * 60 * 60
                msg["mode_managed"] = {
                    "router_ip": data["router_ip"],
                    "netmask": data["router_netmask"],
                    "dhcp": dhcp,
                }
            elif data["mode"] == "unmanaged":
                msg["mode_unmanaged"] = {
                    "lan_type": data["client_proto_4"],
                }
                if data["client_proto_4"] == "static":
                    msg["mode_unmanaged"]["lan_static"] = {
                        "ip": data["client_ip_4"],
                        "netmask": data["client_netmask_4"],
                        "gateway": data["client_gateway_4"],
                    }
                    dns1 = data.get("client_dns1_4")
                    if dns1:
                        msg["mode_unmanaged"]["lan_static"]["dns1"] = dns1

                    dns2 = data.get("client_dns2_4")
                    if dns2:
                        msg["mode_unmanaged"]["lan_static"]["dns2"] = dns2

                elif data["client_proto_4"] == "dhcp":
                    hostname = data.get("client_hostname_4")
                    msg["mode_unmanaged"]["lan_dhcp"] = {
                        "hostname": hostname
                    } if hostname else {}

            res = current_state.backend.perform("lan", "update_settings", msg)
            return "save_result", res  # store {"result": ...} to be used later...

        lan_form.add_callback(lan_form_cb)

        return lan_form
示例#22
0
文件: guest.py 项目: CZ-NIC/foris
    def get_form(self):
        data = {}
        data["guest_enabled"] = self.backend_data["enabled"]
        data["guest_ipaddr"] = self.backend_data["ip"]
        data["guest_netmask"] = self.backend_data["netmask"]
        data["guest_dhcp_enabled"] = self.backend_data["dhcp"]["enabled"]
        data["guest_dhcp_start"] = self.backend_data["dhcp"]["start"]
        data["guest_dhcp_limit"] = self.backend_data["dhcp"]["limit"]
        data["guest_dhcp_leasetime"] = self.backend_data["dhcp"]["lease_time"] // 60 // 60
        data["guest_qos_enabled"] = self.backend_data["qos"]["enabled"]
        data["guest_qos_download"] = self.backend_data["qos"]["download"]
        data["guest_qos_upload"] = self.backend_data["qos"]["upload"]

        if self.data:
            # Update from post
            data.update(self.data)

        guest_form = fapi.ForisForm(
            "guest",
            data,
            validators=[
                validators.DhcpRangeValidator(
                    "guest_netmask",
                    "guest_dhcp_start",
                    "guest_dhcp_limit",
                    gettext(
                        "<strong>DHCP start</strong> and <strong>DHCP max leases</strong> "
                        "does not fit into <strong>Guest network netmask</strong>!"
                    ),
                    [
                        lambda data: not data["guest_enabled"],
                        lambda data: not data["guest_dhcp_enabled"],
                    ],
                )
            ],
        )
        guest_network_section = guest_form.add_section(
            name="guest_network",
            title=_(self.userfriendly_title),
            description=_(
                "Guest network is used for <a href='%(url)s'>guest Wi-Fi</a>. It is separated  "
                "from your ordinary LAN. Devices connected to this network are allowed "
                "to access the internet, but are not allowed to access the configuration "
                "interface of the this device nor the devices in LAN."
            )
            % dict(url=reverse("config_page", page_name="wifi")),
        )
        guest_network_section.add_field(
            Checkbox, name="guest_enabled", label=_("Enable guest network"), default=False
        )
        guest_network_section.add_field(
            Textbox,
            name="guest_ipaddr",
            label=_("Router IP in guest network"),
            default=DEFAULT_GUEST_IP,
            validators=validators.IPv4(),
            hint=_(
                "Router's IP address in the guest network. It is necessary that "
                "the guest network IPs are different from other networks "
                "(LAN, WAN, VPN, etc.)."
            ),
        ).requires("guest_enabled", True)
        guest_network_section.add_field(
            Textbox,
            name="guest_netmask",
            label=_("Guest network netmask"),
            default=DEFAULT_GUEST_MASK,
            validators=validators.IPv4Netmask(),
            hint=_("Network mask of the guest network."),
        ).requires("guest_enabled", True)

        guest_network_section.add_field(
            Checkbox,
            name="guest_dhcp_enabled",
            label=_("Enable DHCP"),
            preproc=lambda val: bool(int(val)),
            default=True,
            hint=_(
                "Enable this option to automatically assign IP addresses to "
                "the devices connected to the router."
            ),
        ).requires("guest_enabled", True)
        guest_network_section.add_field(
            Textbox, name="guest_dhcp_start", label=_("DHCP start")
        ).requires("guest_dhcp_enabled", True)
        guest_network_section.add_field(
            Textbox, name="guest_dhcp_limit", label=_("DHCP max leases")
        ).requires("guest_dhcp_enabled", True)
        guest_network_section.add_field(
            Textbox,
            name="guest_dhcp_leasetime",
            label=_("Lease time (hours)"),
            validators=[validators.InRange(1, 7 * 24)],
        ).requires("guest_dhcp_enabled", True)

        guest_network_section.add_field(
            Checkbox,
            name="guest_qos_enabled",
            label=_("Guest Lan QoS"),
            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_enabled", True)

        guest_network_section.add_field(
            Number,
            name="guest_qos_download",
            label=_("Download (kb/s)"),
            validators=[validators.PositiveInteger()],
            hint=_("Download speed in guest network (in kilobits per second)."),
            default=1024,
        ).requires("guest_qos_enabled", True)
        guest_network_section.add_field(
            Number,
            name="guest_qos_upload",
            label=_("Upload (kb/s)"),
            validators=[validators.PositiveInteger()],
            hint=_("Upload speed in guest network (in kilobits per second)."),
            default=1024,
        ).requires("guest_qos_enabled", True)

        def guest_form_cb(data):
            if data["guest_enabled"]:
                msg = {
                    "enabled": data["guest_enabled"],
                    "ip": data["guest_ipaddr"],
                    "netmask": data["guest_netmask"],
                    "dhcp": {"enabled": data["guest_dhcp_enabled"]},
                    "qos": {"enabled": data["guest_qos_enabled"]},
                }
                if data["guest_dhcp_enabled"]:
                    msg["dhcp"]["start"] = int(data["guest_dhcp_start"])
                    msg["dhcp"]["limit"] = int(data["guest_dhcp_limit"])
                    msg["dhcp"]["lease_time"] = int(data["guest_dhcp_leasetime"]) * 60 * 60

                if data["guest_qos_enabled"]:
                    msg["qos"]["download"] = int(data["guest_qos_download"])
                    msg["qos"]["upload"] = int(data["guest_qos_upload"])
            else:
                msg = {"enabled": False}

            res = current_state.backend.perform("guest", "update_settings", msg)
            return "save_result", res  # store {"result": ...} to be used later...

        guest_form.add_callback(guest_form_cb)

        return guest_form
示例#23
0
文件: remote.py 项目: erdoukki/foris
class RemoteHandler(BaseConfigHandler):

    # Translate status obtained via get_status
    CLIENT_STATUS_VALID = gettext("valid")
    CLIENT_STATUS_REVOKED = gettext("revoked")
    CLIENT_STATUS_EXPIRED = gettext("expired")
    CLIENT_STATUS_GENERATING = gettext("generating")
    CLIENT_STATUS_LOST = gettext("lost")

    TRANSLATION_MAP = {
        "valid": CLIENT_STATUS_VALID,
        "revoked": CLIENT_STATUS_REVOKED,
        "expired": CLIENT_STATUS_EXPIRED,
        "generating": CLIENT_STATUS_GENERATING,
        "lost": CLIENT_STATUS_LOST,
    }

    userfriendly_title = gettext("Remote Access")

    def __init__(self, *args, **kwargs):
        self.backend_data = current_state.backend.perform(
            "remote", "get_settings")
        super().__init__(*args, **kwargs)

    def get_form(self):
        data = {
            "enabled": self.backend_data["enabled"],
            "port": self.backend_data["port"],
            "wan_access": self.backend_data["wan_access"],
        }

        if self.data:
            # Update from post
            data.update(self.data)

        form = fapi.ForisForm("remote", data)
        config_section = form.add_section(name="set_remote",
                                          title=_(self.userfriendly_title))
        config_section.add_field(
            Checkbox,
            name="enabled",
            label=_("Enable remote access"),
        )
        config_section.add_field(
            Checkbox,
            name="wan_access",
            label=_("Accessible via WAN"),
            hint=
            _("If this option is check the device in the WAN network will be able to connect "
              "to the configuration interface. Otherwise only devices on LAN will be able to "
              "access the configuration interface."),
        ).requires("enabled", True)
        config_section.add_field(
            Number,
            name="port",
            label=_("Port"),
            hint=_("A port which will be opened for the remote configuration "
                   "of this device."),
            validator=[InRange(1, 2**16 - 1)],
            default=11884,
        ).requires("enabled", True)

        def form_callback(data):
            msg = {"enabled": data['enabled']}

            if msg["enabled"]:
                msg["port"] = int(data["port"])
                msg["wan_access"] = data['wan_access']

            res = current_state.backend.perform("remote", "update_settings",
                                                msg)
            res['enabled'] = msg['enabled']

            return "save_result", res  # store {"result": ...} to be used later...

        form.add_callback(form_callback)
        return form

    def get_generate_token_form(self, data=None):
        generate_token_form = fapi.ForisForm("generate_remote_token", data)
        token_section = generate_token_form.add_section("generate_token",
                                                        title=None)
        token_section.add_field(
            Textbox,
            name="name",
            label=_("Token name"),
            required=True,
            hint=
            _("The display name for the token. It must be shorter than 64 characters "
              "and must contain only alphanumeric characters, dots, dashes and "
              "underscores."),
            validators=[
                RegExp(_("Token name is invalid."), r'[a-zA-Z0-9_.-]+'),
                LenRange(1, 63)
            ])
        return generate_token_form

    def get_token_id_form(self, data=None):
        token_id_form = fapi.ForisForm("token_id_form", data)
        token_section = token_id_form.add_section("token_id_section",
                                                  title=None)
        token_section.add_field(Textbox,
                                name="token_id",
                                label="",
                                required=True,
                                validators=[
                                    RegExp(_("Token id is invalid."),
                                           r'([a-zA-Z0-9][a-zA-Z0-9])+')
                                ])
        token_section.add_field(
            Textbox,
            name="name",
            label=_("Token name"),
            required=False,
            validators=[
                RegExp(_("Token name is invalid."), r'[a-zA-Z0-9_.-]+'),
                LenRange(1, 63)
            ],
        )
        return token_id_form
示例#24
0
class WifiHandler(BaseConfigHandler):
    userfriendly_title = gettext("Wi-Fi")

    @staticmethod
    def prefixed(index, name):
        return "radio%d-%s" % (index, name)

    def _backend_data_to_form_data(self, backend_data):
        form_data = {}
        for device in backend_data["devices"]:

            def prefixed(name):
                return WifiHandler.prefixed(device["id"], name)

            form_data[prefixed("device_enabled")] = device["enabled"]
            form_data[prefixed("ssid")] = device["SSID"]
            form_data[prefixed("ssid_hidden")] = device["hidden"]
            form_data[prefixed("hwmode")] = device["hwmode"]
            form_data[prefixed("htmode")] = device["htmode"]
            form_data[prefixed("channel")] = str(device["channel"])
            form_data[prefixed("password")] = device["password"]
            form_data[prefixed(
                "guest_enabled")] = device["guest_wifi"]["enabled"]
            form_data[prefixed("guest_ssid")] = device["guest_wifi"]["SSID"]
            form_data[prefixed(
                "guest_password")] = device["guest_wifi"]["password"]

        return form_data

    def _prepare_device_fields(self, section, device, form_data, last=False):
        HINTS = {
            'password':
            _("WPA2 pre-shared key, that is required to connect to the "
              "network. Minimum length is 8 characters.")
        }

        def prefixed(name):
            return WifiHandler.prefixed(device["id"], name)

        # get corresponding band
        bands = [
            e for e in device["available_bands"]
            if e["hwmode"] == form_data["hwmode"]
        ]
        if not bands:
            # wrong hwmode selected pick the first one from available
            band = device["available_bands"][0]
            form_data["hwmode"] = device["available_bands"][0]["hwmode"]
        else:
            band = bands[0]

        wifi_main = section.add_section(
            name=prefixed("set_wifi"),
            title=None,
        )
        wifi_main.add_field(
            Checkbox,
            name=prefixed("device_enabled"),
            label=_("Enable Wi-Fi %s") % (device["id"] + 1),
            default=True,
        )
        wifi_main.add_field(Textbox,
                            name=prefixed("ssid"),
                            label=_("SSID"),
                            required=True,
                            validators=validators.ByteLenRange(
                                1, 32)).requires(prefixed("device_enabled"),
                                                 True)
        wifi_main.add_field(
            Checkbox,
            name=prefixed("ssid_hidden"),
            label=_("Hide SSID"),
            default=False,
            hint=
            _("If set, network is not visible when scanning for available networks."
              )).requires(prefixed("device_enabled"), True)

        wifi_main.add_field(
            Radio,
            name=prefixed("hwmode"),
            label=_("Wi-Fi mode"),
            args=[
                e for e in (("11g", "2.4 GHz (g)"), ("11a", "5 GHz (a)"))
                if e[0] in [b["hwmode"] for b in device["available_bands"]]
            ],
            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("device_enabled"),
                                                    True)

        htmodes = (
            ("NOHT", _("Disabled")),
            ("HT20", _("802.11n - 20 MHz wide channel")),
            ("HT40", _("802.11n - 40 MHz wide channel")),
            ("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("htmode"),
            label=_("802.11n/ac mode"),
            args=[e for e in htmodes if e[0] in band["available_htmodes"]],
            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("device_enabled"), True).requires(
                  prefixed("hwmode"), lambda val: val in ("11g", "11a")
              )  # this req is added to rerender htmodes when hwmode changes

        channels = [("0", _("auto"))] + [
            (str(e["number"]),
             ("%d (%d MHz%s)" %
              (e["number"], e["frequency"], ", DFS" if e["radar"] else "")))
            for e in band["available_channels"]
        ]
        wifi_main.add_field(
            Dropdown,
            name=prefixed("channel"),
            label=_("Network channel"),
            default="0",
            args=channels,
        ).requires(prefixed("device_enabled"), True).requires(
            prefixed("hwmode"), lambda val: val in ("11g", "11a")
        )  # this req is added to rerender channel list when hwmode changes

        wifi_main.add_field(PasswordWithHide,
                            name=prefixed("password"),
                            label=_("Network password"),
                            required=True,
                            validators=validators.ByteLenRange(8, 63),
                            hint=HINTS['password']).requires(
                                prefixed("device_enabled"), True)

        if current_state.app == "config":
            # Guest wi-fi part
            guest_section = wifi_main.add_section(
                name=prefixed("set_guest_wifi"),
                title=_("Guest Wi-Fi"),
                description=_("Set guest Wi-Fi here."))
            guest_section.add_field(
                Checkbox,
                name=prefixed("guest_enabled"),
                label=_("Enable guest Wi-Fi"),
                default=False,
                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 "
                  "Guest network tab</a>. ") %
                dict(url=reverse("config_page", page_name="guest"))).requires(
                    prefixed("device_enabled"), True)
            guest_section.add_field(
                Textbox,
                name=prefixed("guest_ssid"),
                label=_("SSID for guests"),
                required=True,
                validators=validators.ByteLenRange(1, 32),
            ).requires(prefixed("guest_enabled"), True)
            guest_section.add_field(
                PasswordWithHide,
                name=prefixed("guest_password"),
                label=_("Password for guests"),
                required=True,
                default="",
                validators=validators.ByteLenRange(8, 63),
                hint=HINTS['password'],
            ).requires(prefixed("guest_enabled"), True)

        # Horizontal line separating wi-fi cards
        if not last:
            wifi_main.add_field(HorizontalLine,
                                name=prefixed("wifi-separator"),
                                class_="wifi-separator").requires(
                                    prefixed("device_enabled"), True)

    def _form_data_to_backend_data(self, form_data, device_ids):
        res = []

        for dev_id in device_ids:

            def prefixed(name):
                return WifiHandler.prefixed(dev_id, name)

            dev_rec = {"id": dev_id}
            dev_rec["enabled"] = form_data[prefixed("device_enabled")]
            if dev_rec["enabled"]:
                dev_rec["SSID"] = form_data[prefixed("ssid")]
                dev_rec["hidden"] = form_data[prefixed("ssid_hidden")]
                dev_rec["hwmode"] = form_data[prefixed("hwmode")]
                dev_rec["htmode"] = form_data[prefixed("htmode")]
                dev_rec["channel"] = int(form_data[prefixed("channel")])
                dev_rec["guest_wifi"] = {}
                dev_rec["guest_wifi"]["enabled"] = form_data.get(
                    prefixed("guest_enabled"), False)
                dev_rec["password"] = form_data[prefixed("password")]
                if dev_rec["guest_wifi"]["enabled"]:
                    dev_rec["guest_wifi"]["SSID"] = form_data[prefixed(
                        "guest_ssid")]
                    dev_rec["guest_wifi"]["password"] = form_data[prefixed(
                        "guest_password")]

            res.append(dev_rec)

        return {"devices": res}

    def get_form(self):
        backend_data = current_state.backend.perform("wifi", "get_settings")
        form_data = self._backend_data_to_form_data(backend_data)
        if self.data:
            form_data.update(self.data)

        wifi_form = fapi.ForisForm("wifi", form_data)
        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 within the form."))

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

        for idx, device in enumerate(backend_data["devices"]):
            prefix = WifiHandler.prefixed(device["id"], "")
            device_form_data = {
                k[len(prefix):]: v
                for k, v in form_data.items() if k.startswith(prefix)
            }  # prefix removed
            self._prepare_device_fields(
                wifi_section, device, device_form_data,
                len(backend_data["devices"]) - 1 == idx)

        def form_cb(data):
            update_data = self._form_data_to_backend_data(
                data, [e["id"] for e in backend_data["devices"]])
            res = current_state.backend.perform("wifi", "update_settings",
                                                update_data)
            return "save_result", res  # store {"result": ...} to be used later...

        wifi_form.add_callback(form_cb)
        return wifi_form
示例#25
0
文件: lan.py 项目: CZ-NIC/foris
    def get_form(self):
        data = {}
        data["mode"] = self.backend_data["mode"]
        data["router_ip"] = self.backend_data["mode_managed"]["router_ip"]
        data["router_netmask"] = self.backend_data["mode_managed"]["netmask"]
        data["router_dhcp_enabled"] = self.backend_data["mode_managed"]["dhcp"]["enabled"]
        data["router_dhcp_start"] = self.backend_data["mode_managed"]["dhcp"]["start"]
        data["router_dhcp_limit"] = self.backend_data["mode_managed"]["dhcp"]["limit"]
        data["router_dhcp_leasetime"] = self.backend_data["mode_managed"]["dhcp"]["lease_time"] // (
            60 * 60
        )
        data["client_proto_4"] = self.backend_data["mode_unmanaged"]["lan_type"]
        data["client_ip_4"] = self.backend_data["mode_unmanaged"]["lan_static"]["ip"]
        data["client_netmask_4"] = self.backend_data["mode_unmanaged"]["lan_static"]["netmask"]
        data["client_gateway_4"] = self.backend_data["mode_unmanaged"]["lan_static"]["gateway"]
        dns1 = self.backend_data["mode_unmanaged"]["lan_static"].get("dns1")
        if dns1:
            data["client_dns1_4"] = dns1
        dns2 = self.backend_data["mode_unmanaged"]["lan_static"].get("dns2")
        if dns2:
            data["client_dns2_4"] = dns2
        data["client_hostname_4"] = self.backend_data["mode_unmanaged"]["lan_dhcp"].get(
            "hostname", ""
        )

        if self.data:
            # Update from post
            data.update(self.data)

        lan_form = fapi.ForisForm(
            "lan",
            data,
            validators=[
                validators.DhcpRangeValidator(
                    "router_netmask",
                    "router_dhcp_start",
                    "router_dhcp_limit",
                    gettext(
                        "<strong>DHCP start</strong> and <strong>DHCP max leases</strong> "
                        "does not fit into <strong>Network netmask</strong>!"
                    ),
                    [
                        lambda data: data["mode"] != "managed",
                        lambda data: not data["router_dhcp_enabled"],
                    ],
                )
            ],
        )
        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(
            Dropdown,
            name="mode",
            label=_("LAN mode"),
            args=[("managed", _("Router")), ("unmanaged", _("Computer"))],
            hint=_(
                "Router mode means that this devices manages the LAN "
                "(acts as a router, can assing IP addresses, ...). "
                "Computer mode means that this device acts as a client in this network. "
                "It acts in a similar way as WAN, but it has opened ports for configuration "
                "interface and other services."
            ),
            default="managed",
        )

        # managed options
        lan_main.add_field(
            Textbox,
            name="router_ip",
            label=_("Router IP address"),
            validators=validators.IPv4(),
            hint=_("Router's IP address in the inner network."),
        ).requires("mode", "managed")
        lan_main.add_field(
            Textbox,
            name="router_netmask",
            label=_("Network netmask"),
            validators=validators.IPv4Netmask(),
            hint=_("Network mask of the inner network."),
        ).requires("mode", "managed")
        lan_main.add_field(
            Checkbox,
            name="router_dhcp_enabled",
            label=_("Enable DHCP"),
            preproc=lambda val: bool(int(val)),
            default=True,
            hint=_(
                "Enable this option to automatically assign IP addresses to "
                "the devices connected to the router."
            ),
        ).requires("mode", "managed")
        lan_main.add_field(Number, name="router_dhcp_start", label=_("DHCP start")).requires(
            "router_dhcp_enabled", True
        )
        lan_main.add_field(Number, name="router_dhcp_limit", label=_("DHCP max leases")).requires(
            "router_dhcp_enabled", True
        )
        lan_main.add_field(
            Number,
            name="router_dhcp_leasetime",
            label=_("Lease time (hours)"),
            validators=[validators.InRange(1, 7 * 24)],
        ).requires("router_dhcp_enabled", True)

        # unmanaged options
        LAN_DHCP = "dhcp"
        LAN_STATIC = "static"
        LAN_NONE = "none"
        LAN_OPTIONS = (
            (LAN_DHCP, _("DHCP (automatic configuration)")),
            (LAN_STATIC, _("Static IP address (manual configuration)")),
            (LAN_NONE, _("Don't connect this device to LAN")),
        )
        lan_main.add_field(
            Dropdown,
            name="client_proto_4",
            label=_("IPv4 protocol"),
            args=LAN_OPTIONS,
            default=LAN_DHCP,
        ).requires("mode", "unmanaged")
        # unmanaged static
        lan_main.add_field(
            Textbox,
            name="client_ip_4",
            label=_("IPv4 address"),
            required=True,
            validators=validators.IPv4(),
        ).requires("client_proto_4", LAN_STATIC)
        lan_main.add_field(
            Textbox,
            name="client_netmask_4",
            label=_("Network mask"),
            required=True,
            validators=validators.IPv4Netmask(),
        ).requires("client_proto_4", LAN_STATIC)
        lan_main.add_field(
            Textbox,
            name="client_gateway_4",
            label=_("Gateway"),
            required=True,
            validators=validators.IPv4(),
        ).requires("client_proto_4", LAN_STATIC)
        lan_main.add_field(
            Textbox,
            name="client_dns1_4",
            label=_("DNS server 1 (IPv4)"),
            validators=validators.IPv4(),
            hint=_(
                "DNS server address is not required as the built-in "
                "DNS resolver is capable of working without it."
            ),
        ).requires("client_proto_4", LAN_STATIC)
        lan_main.add_field(
            Textbox,
            name="client_dns2_4",
            label=_("DNS server 2 (IPv4)"),
            validators=validators.IPv4(),
            hint=_(
                "DNS server address is not required as the built-in "
                "DNS resolver is capable of working without it."
            ),
        ).requires("client_proto_4", LAN_STATIC)
        # unamanaged dhcp
        lan_main.add_field(
            Textbox,
            name="client_hostname_4",
            label=_("DHCP hostname"),
            validators=validators.Domain(),
            hint=_("Hostname which will be provided to DHCP server."),
        ).requires("client_proto_4", LAN_DHCP)

        def lan_form_cb(data):
            msg = {"mode": data["mode"]}
            if msg["mode"] == "managed":
                dhcp = {"enabled": data["router_dhcp_enabled"]}
                if dhcp["enabled"]:
                    dhcp["start"] = int(data["router_dhcp_start"])
                    dhcp["limit"] = int(data["router_dhcp_limit"])
                    dhcp["lease_time"] = int(data.get("router_dhcp_leasetime", 12)) * 60 * 60
                msg["mode_managed"] = {
                    "router_ip": data["router_ip"],
                    "netmask": data["router_netmask"],
                    "dhcp": dhcp,
                }
            elif data["mode"] == "unmanaged":
                msg["mode_unmanaged"] = {"lan_type": data["client_proto_4"]}
                if data["client_proto_4"] == "static":
                    msg["mode_unmanaged"]["lan_static"] = {
                        "ip": data["client_ip_4"],
                        "netmask": data["client_netmask_4"],
                        "gateway": data["client_gateway_4"],
                    }
                    dns1 = data.get("client_dns1_4")
                    if dns1:
                        msg["mode_unmanaged"]["lan_static"]["dns1"] = dns1

                    dns2 = data.get("client_dns2_4")
                    if dns2:
                        msg["mode_unmanaged"]["lan_static"]["dns2"] = dns2

                elif data["client_proto_4"] == "dhcp":
                    hostname = data.get("client_hostname_4")
                    msg["mode_unmanaged"]["lan_dhcp"] = {"hostname": hostname} if hostname else {}

            res = current_state.backend.perform("lan", "update_settings", msg)
            return "save_result", res  # store {"result": ...} to be used later...

        lan_form.add_callback(lan_form_cb)

        return lan_form
示例#26
0
class UpdaterHandler(BaseConfigHandler):
    userfriendly_title = gettext("Updater")

    APPROVAL_NO = "off"
    APPROVAL_TIMEOUT = "delayed"
    APPROVAL_NEEDED = "on"
    APPROVAL_DEFAULT = APPROVAL_NO
    APPROVAL_DEFAULT_DELAY = 1.0

    def __init__(self, *args, **kwargs):
        super(UpdaterHandler, self).__init__(*args, **kwargs)

        # Check whether updater is supposed to be always on and store the reason why
        self.always_on_reasons: typing.List[str] = []
        for entry_point in pkg_resources.iter_entry_points(
                "updater_always_on"):
            logger.info("Processing 'updater_always_on' for '%s' plugin",
                        entry_point.name)
            reason: typing.Option[str] = entry_point.load()()
            if reason:
                self.always_on_reasons.append(reason)

        self.backend_data = current_state.backend.perform(
            "updater", "get_settings", {"lang": current_state.language})
        # store setting required for rendering
        self.current_approval = self.backend_data["approval"]
        # update can be in 3 states: True, False, None
        # None means that it is not set in this case we want to prefill True
        self.updater_enabled = False if self.backend_data[
            "enabled"] is False else True
        self.approval_setting_status = self.backend_data["approval_settings"][
            "status"]
        self.approval_setting_delay = self.backend_data[
            "approval_settings"].get("delay", self.APPROVAL_DEFAULT_DELAY)

    def get_form(self):
        data = copy.deepcopy(self.backend_data)

        data["enabled"] = "0" if data["enabled"] is False else "1"
        data["approval_status"] = data["approval_settings"]["status"]
        if "delay" in data["approval_settings"]:
            data["approval_delay"] = "%.1f" % (
                data["approval_settings"]["delay"] / 24.0)
        for userlist in [e for e in data["user_lists"] if not e["hidden"]]:
            data["install_%s" % userlist["name"]] = userlist["enabled"]
        for lang in data["languages"]:
            data["language_%s" % lang["code"]] = lang["enabled"]

        if self.data:
            # Update from post
            data.update(self.data)
            self.updater_enabled = True if data["enabled"] == "1" else False
            self.approval_setting_status = data["approval_status"]
            self.approval_setting_delay = data.get("approval_delay",
                                                   self.APPROVAL_DEFAULT_DELAY)

        form = fapi.ForisForm("updater", data)
        main_section = form.add_section(
            name="main",
            title=_(self.userfriendly_title),
            description=_("Updater is a service that keeps all TurrisOS "
                          "software up to date. Apart from the standard "
                          "installation, you can optionally select bundles of "
                          "additional software that'd be installed on the "
                          "router. This software can be selected from the "
                          "following list. "
                          "Please note that only software that is part of "
                          "TurrisOS or that has been installed from a package "
                          "list is maintained by Updater. Software that has "
                          "been installed manually or using opkg is not "
                          "affected."),
        )
        main_section.add_field(
            Radio,
            name="enabled",
            label=_("I agree"),
            default="1",
            args=(
                ("1", _("Use automatic updates (recommended)")),
                ("0", _("Turn automatic updates off")),
            ),
        )

        approval_section = main_section.add_section(
            name="approvals", title=_("Update approvals"))
        approval_section.add_field(
            RadioSingle,
            name=UpdaterHandler.APPROVAL_NO,
            group="approval_status",
            label=_("Automatic installation"),
            hint=_("Updates will be installed without user's intervention."),
            default=data["approval_status"],
        )

        approval_section.add_field(
            RadioSingle,
            name=UpdaterHandler.APPROVAL_TIMEOUT,
            group="approval_status",
            label=_("Delayed updates"),
            hint=_("Updates will be installed with an adjustable delay. "
                   "You can also approve them manually."),
            default=data["approval_status"],
        )
        approval_section.add_field(
            Textbox,
            name="approval_delay",
            validators=[validators.FloatRange(0.1, 31.0)],
            default=UpdaterHandler.APPROVAL_DEFAULT_DELAY,
            required=True,
        ).requires(UpdaterHandler.APPROVAL_TIMEOUT,
                   UpdaterHandler.APPROVAL_TIMEOUT).requires(
                       UpdaterHandler.APPROVAL_NO,
                       UpdaterHandler.APPROVAL_TIMEOUT).requires(
                           UpdaterHandler.APPROVAL_NEEDED,
                           UpdaterHandler.APPROVAL_TIMEOUT)

        approval_section.add_field(
            RadioSingle,
            name=UpdaterHandler.APPROVAL_NEEDED,
            group="approval_status",
            label=_("Update approval needed"),
            hint=
            _("You have to approve the updates, otherwise they won't be installed."
              ),
            default=data["approval_status"],
        )

        package_lists_main = main_section.add_section(
            name="select_package_lists", title=None)
        for userlist in [e for e in data["user_lists"] if not e["hidden"]]:
            package_lists_main.add_field(
                Checkbox,
                name="install_%s" % userlist["name"],
                label=userlist["title"],
                hint=userlist["msg"],
            ).requires("enabled", "1")

        language_lists_main = main_section.add_section(
            name="select_languages",
            title=_(
                "If you want to use other language than English you can select it from the "
                "following list:"),
        )
        for lang in data["languages"]:
            language_lists_main.add_field(Checkbox,
                                          name="language_%s" % lang["code"],
                                          label=lang["code"].upper())

        if self.backend_data["approval"]["present"]:
            # field for hidden approval
            current_approval_section = main_section.add_section(
                name="current_approval", title="")
            current_approval_section.add_field(
                Hidden,
                name="approval-id",
                default=self.backend_data["approval"]["hash"])

        # this will be filled according to action
        main_section.add_field(Hidden, name="target")

        def form_cb(data):
            data["enabled"] = True if data["enabled"] == "1" else False
            if data["enabled"] and data["target"] == "save":
                if data[self.APPROVAL_NEEDED] == self.APPROVAL_NEEDED:
                    data["approval_settings"] = {
                        "status": self.APPROVAL_NEEDED
                    }
                elif data[self.APPROVAL_TIMEOUT] == self.APPROVAL_TIMEOUT:
                    data["approval_settings"] = {
                        "status": self.APPROVAL_TIMEOUT
                    }
                    data["approval_settings"]["delay"] = int(
                        float(data["approval_delay"]) * 24)
                elif data[self.APPROVAL_NO] == self.APPROVAL_NO:
                    data["approval_settings"] = {"status": self.APPROVAL_NO}

                if self.always_on_reasons:  # don't disable updater when there are reasons
                    data["enabled"] = True

                languages = [
                    k[9:] for k, v in data.items()
                    if v and k.startswith("language_")
                ]
                user_lists = [
                    k[8:] for k, v in data.items()
                    if v and k.startswith("install_")
                ]
                # merge with enabled hidden user lists
                user_lists += [
                    e["name"] for e in self.backend_data["user_lists"]
                    if e["hidden"] and e["enabled"]
                ]

                res = current_state.backend.perform(
                    "updater",
                    "update_settings",
                    {
                        "enabled": True,
                        "approval_settings": data["approval_settings"],
                        "user_lists": user_lists,
                        "languages": languages,
                    },
                )
            elif data["enabled"] and data["target"] in ["grant", "deny"]:
                res = current_state.backend.perform(
                    "updater",
                    "resolve_approval",
                    {
                        "hash": data["approval-id"],
                        "solution": data["target"]
                    },
                )
            else:
                res = current_state.backend.perform("updater",
                                                    "update_settings",
                                                    {"enabled": False})

            res["target"] = data["target"]
            res["enabled"] = data["enabled"]
            return "save_result", res

        form.add_callback(form_cb)
        return form
示例#27
0
class NotificationsHandler(BaseConfigHandler):
    userfriendly_title = gettext("Notifications")

    def get_form(self):
        data = current_state.backend.perform("router_notifications",
                                             "get_settings")
        data["enable_smtp"] = data["emails"]["enabled"]
        data["use_turris_smtp"] = "1" if data["emails"][
            "smtp_type"] == "turris" else "0"
        data["to"] = " ".join(data["emails"]["common"]["to"])
        data["sender_name"] = data["emails"]["smtp_turris"]["sender_name"]
        data["severity"] = data["emails"]["common"]["severity_filter"]
        data["news"] = data["emails"]["common"]["send_news"]
        data["from"] = data["emails"]["smtp_custom"]["from"]
        data["server"] = data["emails"]["smtp_custom"]["host"]
        data["port"] = data["emails"]["smtp_custom"]["port"]
        data["security"] = data["emails"]["smtp_custom"]["security"]
        data["username"] = data["emails"]["smtp_custom"]["username"]
        data["password"] = data["emails"]["smtp_custom"]["password"]
        data["delay"] = str(data["reboots"]["delay"])
        data["reboot_time"] = data["reboots"]["time"]

        if self.data:
            # Update from post
            data.update(self.data)

        notifications_form = fapi.ForisForm("notifications", data)

        notifications = notifications_form.add_section(
            name="notifications", title=_("Notifications settings"))
        # notifications settings
        notifications.add_field(Checkbox,
                                name="enable_smtp",
                                label=_("Enable notifications"),
                                default=False)

        notifications.add_field(
            Radio,
            name="use_turris_smtp",
            label=_("SMTP provider"),
            default="0",
            args=(("1", _("Turris")), ("0", _("Custom"))),
            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"),
            hint=
            _("Email address of recipient. Separate multiple addresses by spaces."
              ),
            required=True,
            validators=[
                validators.RegExp(
                    _("Doesn't contain a list of emails separated by spaces"),
                    r"^([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)( +[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+ *)*$",
                )
            ],
        ).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.'),
            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"),
                                args=SEVERITY_OPTIONS,
                                default=1).requires("enable_smtp", True)
        notifications.add_field(
            Checkbox,
            name="news",
            label=_("Send news"),
            hint=_("Send emails about new features."),
            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 send from."),
            required=True,
            placeholder="*****@*****.**",
        ).requires("enable_smtp", True).requires("use_turris_smtp", "0")
        smtp.add_field(Textbox,
                       name="server",
                       label=_("Server address"),
                       placeholder="example.com").requires(
                           "enable_smtp",
                           True).requires("use_turris_smtp", "0")
        smtp.add_field(
            Number,
            name="port",
            label=_("Server 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"),
                       args=SECURITY_OPTIONS,
                       default="none").requires("enable_smtp", True).requires(
                           "use_turris_smtp", "0")

        smtp.add_field(Textbox, name="username", label=_("Username")).requires(
            "enable_smtp", True).requires("use_turris_smtp", "0")
        smtp.add_field(Password,
                       name="password", label=_("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."),
            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."),
            validators=[validators.Time()],
            required=True,
        )

        def notifications_form_cb(data):
            msg = {
                "reboots": {
                    "delay": int(data["delay"]),
                    "time": data["reboot_time"]
                },
                "emails": {
                    "enabled": data["enable_smtp"]
                },
            }
            if data["enable_smtp"]:
                msg["emails"]["smtp_type"] = (
                    "turris" if data["use_turris_smtp"] == "1" else "custom")
                msg["emails"]["common"] = {
                    "to": [e for e in data["to"].split(" ") if e],
                    "severity_filter": int(data["severity"]),
                    "send_news": data["news"],
                }
                if msg["emails"]["smtp_type"] == "turris":
                    msg["emails"]["smtp_turris"] = {
                        "sender_name": data["sender_name"]
                    }
                elif msg["emails"]["smtp_type"] == "custom":
                    msg["emails"]["smtp_custom"] = {
                        "from": data["from"],
                        "host": data["server"],
                        "port": int(data["port"]),
                        "security": data["security"],
                        "username": data["username"],
                        "password": data["password"],
                    }

            res = current_state.backend.perform("router_notifications",
                                                "update_settings", msg)

            return "save_result", res  # store {"result": ...} to be used later...

        notifications_form.add_callback(notifications_form_cb)

        return notifications_form
示例#28
0
class DNSHandler(BaseConfigHandler):
    """
    DNS-related settings
    """

    userfriendly_title = gettext("DNS")

    def get_form(self):
        data = current_state.backend.perform("dns", "get_settings")
        available_forwarders = [[e["name"], e["description"]]
                                for e in data["available_forwarders"]]
        data["dnssec_disabled"] = not data["dnssec_enabled"]
        if self.data:
            # Update from post
            data.update(self.data)
            data["dnssec_enabled"] = not self.data.get("dnssec_disabled",
                                                       False)

        dns_form = fapi.ForisForm("dns", data)
        dns_main = dns_form.add_section(name="set_dns",
                                        title=_(self.userfriendly_title))
        dns_main.add_field(
            Checkbox,
            name="forwarding_enabled",
            label=_("Use forwarding"),
            preproc=lambda val: bool(int(val)),
        )
        available_forwarders = sorted(available_forwarders, key=lambda x: x[0])
        # fill in text for forwarder (first with "" name)
        available_forwarders[0][1] = _("Use provider's DNS resolver")
        dns_main.add_field(Dropdown,
                           name="forwarder",
                           label=_("DNS Forwarder"),
                           args=available_forwarders).requires(
                               "forwarding_enabled", True)

        dns_main.add_field(
            Checkbox,
            name="dnssec_disabled",
            label=_("Disable DNSSEC"),
            preproc=lambda val: bool(int(val)),
            default=False,
        )

        dns_main.add_field(
            Checkbox,
            name="dns_from_dhcp_enabled",
            label=_("Enable DHCP clients in DNS"),
            hint=_("This will enable your DNS resolver to place DHCP client's "
                   "names among the local DNS records."),
            preproc=lambda val: bool(int(val)),
            default=False,
        )
        dns_main.add_field(
            Textbox,
            name="dns_from_dhcp_domain",
            label=_("Domain of DHCP clients in DNS"),
            hint=_(
                'This domain will be used as suffix. E.g. The result for client "android-123" '
                'and domain "my.lan" will be "android-123.my.lan".'),
            validators=[validators.Domain()],
        ).requires("dns_from_dhcp_enabled", True)

        def dns_form_cb(data):
            msg = {
                "dnssec_enabled": not data.get("dnssec_disabled", False),
                "forwarding_enabled": data["forwarding_enabled"],
                "dns_from_dhcp_enabled": data["dns_from_dhcp_enabled"],
            }
            if "dns_from_dhcp_domain" in data:
                msg["dns_from_dhcp_domain"] = data["dns_from_dhcp_domain"]
            if data["forwarding_enabled"]:
                msg["forwarder"] = data.get("forwarder", "")
            res = current_state.backend.perform("dns", "update_settings", msg)
            return "save_result", res  # store {"result": ...} to be used later...

        dns_form.add_callback(dns_form_cb)
        return dns_form
示例#29
0
class UpdaterHandler(BaseConfigHandler):
    userfriendly_title = gettext("Updater")

    APPROVAL_NO = "off"
    APPROVAL_TIMEOUT = "delayed"
    APPROVAL_NEEDED = "on"
    APPROVAL_DEFAULT = APPROVAL_NO
    APPROVAL_DEFAULT_DELAY = 24

    def __init__(self, *args, **kwargs):
        super(UpdaterHandler, self).__init__(*args, **kwargs)
        agreed = current_state.backend.perform(
            "data_collect", "get", raise_exception_on_failure=False)
        self.agreed_collect = False if agreed is None else agreed["agreed"]

        self.backend_data = current_state.backend.perform(
            "updater", "get_settings", {"lang": current_state.language})
        # store setting required for rendering
        self.current_approval = self.backend_data["approval"]
        self.updater_enabled = self.backend_data["enabled"]
        self.approval_setting_status = self.backend_data["approval_settings"][
            "status"]
        self.approval_setting_delay = self.backend_data[
            "approval_settings"].get("delay", self.APPROVAL_DEFAULT_DELAY)

    def get_form(self):
        data = copy.deepcopy(self.backend_data)

        data["enabled"] = "1" if data["enabled"] else "0"
        data["approval_status"] = data["approval_settings"]["status"]
        if "delay" in data["approval_settings"]:
            data["approval_delay"] = data["approval_settings"]["delay"]
        for userlist in [e for e in data['user_lists'] if not e["hidden"]]:
            data["install_%s" % userlist["name"]] = userlist["enabled"]
        for lang in data["languages"]:
            data["language_%s" % lang["code"]] = lang["enabled"]

        if self.data:
            # Update from post
            data.update(self.data)
            self.updater_enabled = True if data["enabled"] == "1" else False
            self.approval_setting_status = data["approval_status"]
            self.approval_setting_delay = data.get("approval_delay",
                                                   self.APPROVAL_DEFAULT_DELAY)

        form = fapi.ForisForm("updater", data)
        main_section = form.add_section(
            name="main",
            title=_(self.userfriendly_title),
            description=_("Updater is a service that keeps all TurrisOS "
                          "software up to date. Apart from the standard "
                          "installation, you can optionally select bundles of "
                          "additional software that'd be installed on the "
                          "router. This software can be selected from the "
                          "following list. "
                          "Please note that only software that is part of "
                          "TurrisOS or that has been installed from a package "
                          "list is maintained by Updater. Software that has "
                          "been installed manually or using opkg is not "
                          "affected."))
        main_section.add_field(
            Radio,
            name="enabled",
            label=_("I agree"),
            default="1",
            args=(("1", _("Use automatic updates (recommended)")),
                  ("0", _("Turn automatic updates off"))),
        )

        approval_section = main_section.add_section(
            name="approvals", title=_("Update approvals"))
        approval_section.add_field(
            RadioSingle,
            name=UpdaterHandler.APPROVAL_NO,
            group="approval_status",
            label=_("Automatic installation"),
            hint=_("Updates will be installed without user's intervention."),
            default=data["approval_status"],
        )

        approval_section.add_field(
            RadioSingle,
            name=UpdaterHandler.APPROVAL_TIMEOUT,
            group="approval_status",
            label=_("Delayed updates"),
            hint=_("Updates will be installed with an adjustable delay. "
                   "You can also approve them manually."),
            default=data["approval_status"],
        )
        approval_section.add_field(
            Number,
            name="approval_delay",
            validators=[validators.InRange(1, 24 * 7)],
            default=UpdaterHandler.APPROVAL_DEFAULT_DELAY,
            min=1,
            max=24 * 7,
            required=True,
        ).requires(UpdaterHandler.APPROVAL_TIMEOUT,
                   UpdaterHandler.APPROVAL_TIMEOUT).requires(
                       UpdaterHandler.APPROVAL_NO,
                       UpdaterHandler.APPROVAL_TIMEOUT).requires(
                           UpdaterHandler.APPROVAL_NEEDED,
                           UpdaterHandler.APPROVAL_TIMEOUT)

        approval_section.add_field(
            RadioSingle,
            name=UpdaterHandler.APPROVAL_NEEDED,
            group="approval_status",
            label=_("Update approval needed"),
            hint=
            _("You have to approve the updates, otherwise they won't be installed."
              ),
            default=data["approval_status"],
        )

        package_lists_main = main_section.add_section(
            name="select_package_lists",
            title=None,
        )
        for userlist in [e for e in data['user_lists'] if not e["hidden"]]:
            package_lists_main.add_field(Checkbox,
                                         name="install_%s" % userlist["name"],
                                         label=userlist["title"],
                                         hint=userlist["msg"]).requires(
                                             "enabled", "1")

        language_lists_main = main_section.add_section(
            name="select_languages",
            title=_(
                "If you want to use other language than English you can select it from the "
                "following list:"))
        for lang in data["languages"]:
            language_lists_main.add_field(Checkbox,
                                          name="language_%s" % lang["code"],
                                          label=lang["code"].upper())

        if self.backend_data["approval"]["present"]:
            # field for hidden approval
            current_approval_section = main_section.add_section(
                name="current_approval", title="")
            current_approval_section.add_field(
                Hidden,
                name="approval-id",
                default=self.backend_data["approval"]["hash"])

        # this will be filled according to action
        main_section.add_field(Hidden, name="target")

        def form_cb(data):
            data["enabled"] = True if data["enabled"] == "1" else False
            if data["enabled"] and data["target"] == "save":
                if data[self.APPROVAL_NEEDED] == self.APPROVAL_NEEDED:
                    data["approval_settings"] = {
                        "status": self.APPROVAL_NEEDED
                    }
                elif data[self.APPROVAL_TIMEOUT] == self.APPROVAL_TIMEOUT:
                    data["approval_settings"] = {
                        "status": self.APPROVAL_TIMEOUT
                    }
                    data["approval_settings"]["delay"] = int(
                        data["approval_delay"])
                elif data[self.APPROVAL_NO] == self.APPROVAL_NO:
                    data["approval_settings"] = {"status": self.APPROVAL_NO}

                if self.agreed_collect:
                    data["enabled"] = True

                languages = [
                    k[9:] for k, v in data.items()
                    if v and k.startswith("language_")
                ]
                user_lists = [
                    k[8:] for k, v in data.items()
                    if v and k.startswith("install_")
                ]
                # merge with enabled hidden user lists
                user_lists += [
                    e["name"] for e in self.backend_data["user_lists"]
                    if e["hidden"] and e["enabled"]
                ]

                res = current_state.backend.perform(
                    "updater", "update_settings", {
                        "enabled": True,
                        "approval_settings": data["approval_settings"],
                        "user_lists": user_lists,
                        "languages": languages,
                    })
            elif data["enabled"] and data["target"] in ["grant", "deny"]:
                res = current_state.backend.perform(
                    "updater", "resolve_approval", {
                        "hash": data["approval-id"],
                        "solution": data["target"]
                    })
            else:
                res = current_state.backend.perform("updater",
                                                    "update_settings", {
                                                        "enabled": False,
                                                    })

            res["target"] = data["target"]
            return "save_result", res

        form.add_callback(form_cb)
        return form
示例#30
0
class PasswordHandler(BaseConfigHandler):
    """
    Setting the password
    """

    userfriendly_title = gettext("Password")

    def __init__(self, *args, **kwargs):
        self.change = kwargs.pop("change", False)
        super(PasswordHandler, self).__init__(*args, **kwargs)

    def get_form(self):
        # form definitions
        pw_form = fapi.ForisForm("password", self.data)
        pw_main = pw_form.add_section(name="passwords",
                                      title=_(self.userfriendly_title))
        if self.change:
            pw_main.add_field(Password,
                              name="old_password",
                              label=_("Current Foris password"))
            label_pass1 = _("New password")
            label_pass2 = _("New password (repeat)")
        else:
            label_pass1 = _("Password")
            label_pass2 = _("Password (repeat)")

        pw_foris = pw_form.add_section(
            name="foris_pw",
            title=_("for Foris web interface"),
            description=_("Set your password for this administration "
                          "interface. The password must be at least 6 "
                          "characters long."))
        pw_foris.add_field(Password,
                           name="password",
                           label=label_pass1,
                           required=True,
                           validators=validators.LenRange(6, 128))
        pw_foris.add_field(Password,
                           name="password_validation",
                           label=label_pass2,
                           required=True,
                           validators=validators.EqualTo(
                               "password", "password_validation",
                               _("Passwords are not equal.")))
        system_pw = pw_form.add_section(
            name="system_pw",
            title=_("for Advanced administration"),
            description=
            _("In order to access the advanced configuration options which are "
              "not available 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 via SSH.") % {
                  'host': bottle.request.get_header('host'),
                  'path': 'cgi-bin/luci'
              })
        SYSTEM_PW_SKIP = 'skip'
        SYSTEM_PW_SAME = 'same'
        SYSTEM_PW_CUSTOM = 'custom'
        SYSTEM_PW_OPTIONS = (
            (SYSTEM_PW_SKIP, _("Don't set this password")),
            (SYSTEM_PW_SAME, _("Use Foris password")),
            (SYSTEM_PW_CUSTOM, _("Use other password")),
        )
        system_pw.add_field(
            Dropdown,
            name="set_system_pw",
            label=_("Advanced administration"),
            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.)"),
            args=SYSTEM_PW_OPTIONS,
            default=SYSTEM_PW_SKIP,
        )
        system_pw.add_field(Password,
                            name="system_password",
                            label=_("Password"),
                            required=True,
                            validators=validators.LenRange(6, 128)).requires(
                                "set_system_pw", SYSTEM_PW_CUSTOM)
        system_pw.add_field(Password,
                            name="system_password_validation",
                            label=_("New password (repeat)"),
                            required=True,
                            validators=validators.EqualTo(
                                "system_password",
                                "system_password_validation",
                                _("Passwords are not equal."))).requires(
                                    "set_system_pw", SYSTEM_PW_CUSTOM)

        def pw_form_cb(data):
            if self.change:
                if not check_password(data['old_password']):
                    return "save_result", {'wrong_old_password': True}

            encoded_password = base64.b64encode(
                data["password"].encode("utf-8")).decode("utf-8")

            result = current_state.backend.perform("password", "set", {
                "password": encoded_password,
                "type": "foris"
            })["result"]
            res = {"foris_password_no_error": result}

            if data['set_system_pw'] == SYSTEM_PW_SAME:
                result = current_state.backend.perform("password", "set", {
                    "password": encoded_password,
                    "type": "system"
                })["result"]
                res["system_password_no_error"] = result
            elif data['set_system_pw'] == SYSTEM_PW_CUSTOM:
                encoded_pw = base64.b64encode(
                    data["system_password"].encode("utf-8")).decode("utf-8")
                result = current_state.backend.perform("password", "set", {
                    "password": encoded_pw,
                    "type": "system"
                })["result"]
                res["system_password_no_error"] = result

            return "save_result", res

        pw_form.add_callback(pw_form_cb)

        return pw_form
示例#31
0
class DataCollectPluginPage(ConfigPageMixin, DataCollectPluginConfigHandler):
    userfriendly_title = gettext("Data collection")

    slug = "data_collect"
    menu_order = 80
    template = "data_collect/data_collect"
    template_type = "jinja2"

    SENDING_STATUS_TRANSLATION = {
        'online': gettext("Online"),
        'offline': gettext("Offline"),
        'unknown': gettext("Unknown status"),
    }

    def save(self, *args, **kwargs):
        super().save(no_messages=True, *args, **kwargs)
        result = self.form.callback_results.get('result', False)
        if result:
            messages.success(_("Configuration was successfully saved."))
        else:
            messages.error(
                _("Failed to update emulated services. Note that you might need to wait till "
                  "ucollect is properly installed."))
        return result

    def _prepare_render_args(self, args):
        args['PLUGIN_NAME'] = DataCollectPlugin.PLUGIN_NAME
        args['PLUGIN_STYLES'] = DataCollectPlugin.PLUGIN_STYLES
        args['PLUGIN_STATIC_SCRIPTS'] = DataCollectPlugin.PLUGIN_STATIC_SCRIPTS
        args[
            'PLUGIN_DYNAMIC_SCRIPTS'] = DataCollectPlugin.PLUGIN_DYNAMIC_SCRIPTS

    def render(self, **kwargs):
        self._prepare_render_args(kwargs)

        status = kwargs.pop("status", None)
        updater_data = current_state.backend.perform("updater", "get_enabled")
        kwargs['updater_disabled'] = not updater_data["enabled"]

        if updater_data["enabled"]:
            collect_data = current_state.backend.perform("data_collect", "get")
            firewall_status = collect_data["firewall_status"]
            firewall_status["seconds_ago"] = int(time.time() -
                                                 firewall_status["last_check"])
            firewall_status["datetime"] = datetime.fromtimestamp(
                firewall_status["last_check"])
            firewall_status["state_trans"] = self.SENDING_STATUS_TRANSLATION[
                firewall_status["state"]]

            ucollect_status = collect_data["ucollect_status"]
            ucollect_status["seconds_ago"] = int(time.time() -
                                                 ucollect_status["last_check"])
            ucollect_status["datetime"] = datetime.fromtimestamp(
                ucollect_status["last_check"])
            ucollect_status["state_trans"] = self.SENDING_STATUS_TRANSLATION[
                ucollect_status["state"]]

            kwargs["ucollect_status"] = ucollect_status
            kwargs["firewall_status"] = firewall_status

            if collect_data["agreed"]:
                handler = CollectionToggleHandler(bottle.request.POST.decode())
                kwargs['collection_toggle_form'] = handler.form
                kwargs['agreed'] = collect_data["agreed"]
            else:
                email = bottle.request.POST.decode().get(
                    "email",
                    bottle.request.GET.decode().get("email", ""))
                handler = RegistrationCheckHandler({"email": email})
                kwargs['registration_check_form'] = handler.form

        return self.default_template(form=self.form,
                                     title=self.userfriendly_title,
                                     description=None,
                                     status=status,
                                     **kwargs)

    def _action_check_registration(self):
        handler = RegistrationCheckHandler(bottle.request.POST.decode())
        if not handler.save():
            messages.warning(_("There were some errors in your input."))
            return self.render(registration_check_form=handler.form)

        email = handler.data["email"]

        result = handler.form.callback_results
        kwargs = {}
        if not result["success"]:
            messages.error(
                _("An error ocurred when checking the registration: "
                  "<br><pre>%(error)s</pre>" % dict(error=result["error"])))
            return self.render()
        else:
            if result["status"] == "owned":
                messages.success(
                    _("Registration for the entered email is valid. "
                      "Now you can enable the data collection."))
                collection_toggle_handler = CollectionToggleHandler(
                    bottle.request.POST.decode())
                kwargs[
                    'collection_toggle_form'] = collection_toggle_handler.form
            elif result["status"] == "foreign":
                messages.warning(
                    _('This router is currently assigned to a different email address. Please '
                      'continue to the <a href="%(url)s">Turris website</a> and use the '
                      'registration code <strong>%(reg_num)s</strong> for a re-assignment to your '
                      'email address.') %
                    dict(url=result["url"],
                         reg_num=result["registration_number"]))
                bottle.redirect(
                    reverse("config_page", page_name="data_collect") + "?" +
                    urlencode({"email": email}))
            elif result["status"] == "free":
                messages.info(
                    _('This email address is not registered yet. Please continue to the '
                      '<a href="%(url)s">Turris website</a> and use the registration code '
                      '<strong>%(reg_num)s</strong> to create a new account.')
                    % dict(url=result["url"],
                           reg_num=result["registration_number"]))
                bottle.redirect(
                    reverse("config_page", page_name="data_collect") + "?" +
                    urlencode({"email": email}))
            elif result["status"] == "not_found":
                messages.error(
                    _('Router failed to authorize. Please try to validate our email later.'
                      ))
                bottle.redirect(
                    reverse("config_page", page_name="data_collect") + "?" +
                    urlencode({"email": email}))
        return self.render(status=result["status"],
                           registration_url=result["url"],
                           reg_num=result["registration_number"],
                           **kwargs)

    def _action_toggle_collecting(self):
        if bottle.request.method != 'POST':
            messages.error(_("Wrong HTTP method."))
            bottle.redirect(reverse("config_page", page_name="data_collect"))

        handler = CollectionToggleHandler(bottle.request.POST.decode())
        if handler.save():
            messages.success(_("Configuration was successfully saved."))
            bottle.redirect(reverse("config_page", page_name="data_collect"))

        messages.warning(_("There were some errors in your input."))
        return super().render(collection_toggle_form=handler.form)

    def call_action(self, action):
        if action == "check_registration":
            return self._action_check_registration()
        elif action == "toggle_collecting":
            return self._action_toggle_collecting()
        raise bottle.HTTPError(404, "Unknown action")
示例#32
0
class UnifiedTimeHandler(BaseConfigHandler):
    """
    Setting of the region information and time
    """
    userfriendly_title = gettext("Region and time")

    def __init__(self, *args, **kwargs):
        self.backend_data = current_state.backend.perform(
            "time", "get_settings")
        super().__init__(*args, **kwargs)

    def get_form(self):
        data = copy.deepcopy(self.backend_data)
        data["zonename"] = "%s/%s" % (data["region"], data["city"])
        data["how_to_set_time"] = data["time_settings"]["how_to_set_time"]
        formatted_date = datetime.strptime(
            data["time_settings"]["time"],
            "%Y-%m-%dT%H:%M:%S.%f").strftime("%Y-%m-%d %H:%M:%S")
        data["time"] = formatted_date
        data["ntp_time"] = formatted_date

        if self.data:
            # update from post
            data.update(self.data)

            if bottle.request.is_xhr:
                # xhr won't update the settings, so use current time to update it
                data["time"] = formatted_date
                data["ntp_time"] = formatted_date

        region_and_time_form = fapi.ForisForm("region_and_time", data)

        # section just for common description
        main_section = region_and_time_form.add_section(
            name="region_and_time",
            title=_(self.userfriendly_title),
            description=
            _("It is important for your device to have the correct time set. "
              "If your device's time is delayed, the procedure of SSL certificate verification "
              "might not work correctly."))
        # region section
        region_section = main_section.add_section(
            name="timezone",
            title=_("Region settings"),
            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 = current_state.language

        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

        regions = localized_sorted(((x, _(x)) for x in tzinfo.regions),
                                   lang=lang,
                                   key=lambda x: x[1])
        region_section.add_field(Dropdown,
                                 name="region",
                                 label=_("Continent or ocean"),
                                 required=True,
                                 args=regions)

        # Get region and offer available countries
        region = region_and_time_form.current_data.get('region')
        countries = construct_args(tzinfo.countries_in_region(region),
                                   lambda x: _(tzinfo.countries[x]))
        region_section.add_field(
            Dropdown,
            name="country",
            label=_("Country"),
            required=True,
            default=tzinfo.get_country_for_tz(data["zonename"]),
            args=countries,
        ).requires("region")

        # Get country and offer available timezones
        country = region_and_time_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)
        region_section.add_field(Dropdown,
                                 name="zonename",
                                 label=_("Timezone"),
                                 required=True,
                                 default=data["zonename"],
                                 args=timezones).requires(
                                     "country",
                                     lambda x: country and x is not None)

        # time section
        time_section = main_section.add_section(
            name="time",
            title=_("Time settings"),
            description=
            _("Time should be up-to-date otherwise DNS and other services might not work properly."
              ))
        time_section.add_field(
            Dropdown,
            name="how_to_set_time",
            label=_("How to set time"),
            description=_(
                "Choose method to store current time into the router."),
            default="ntp",
            args=(
                ("ntp", _("via ntp")),
                ("manual", _("manually")),
            ))
        time_section.add_field(
            Textbox,
            name="time",
            validators=validators.Datetime(),
            label=_("Time"),
            hint=_("Time in YYYY-MM-DD HH:MM:SS format."),
        ).requires("how_to_set_time", "manual")
        time_section.add_field(
            Textbox,
            name="ntp_time",
            label=_("Time"),
        ).requires("how_to_set_time", "ntp")

        def region_form_cb(data):
            region, city = data["zonename"].split("/")
            msg = {
                "city": city,
                "region": region,
                "timezone": tzinfo.get_zoneinfo_for_tz(data["zonename"]),
                "time_settings": {
                    "how_to_set_time": data["how_to_set_time"],
                }
            }

            if data["how_to_set_time"] == "manual":
                msg["time_settings"]["time"] = datetime.strptime(
                    data["time"],
                    "%Y-%m-%d %H:%M:%S").replace(microsecond=1).isoformat()

            res = current_state.backend.perform("time", "update_settings", msg)
            return "save_result", res  # store {"result": ...} to be used later...

        region_and_time_form.add_callback(region_form_cb)
        return region_and_time_form