diff --git a/actions/__init__.py b/actions/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/actions/pagekite b/actions/pagekite
index 0a9024158..56b692a86 100755
--- a/actions/pagekite
+++ b/actions/pagekite
@@ -18,7 +18,7 @@
#
"""
-Configuration helper for Plint PageKite interface
+Configuration helper for Plinth PageKite interface
Unfortunately there is no python3 package for augeas yet
"""
diff --git a/actions/pagekite_util.py b/actions/pagekite_util.py
index b3568d43a..b96786fe4 100644
--- a/actions/pagekite_util.py
+++ b/actions/pagekite_util.py
@@ -19,11 +19,14 @@
"""
Utilities for configuring PageKite.
-
-The variables/functions defined here are used by both the action script
-and the plinth pagekite module.
"""
-# ATTENTION: This file has to be both python2 and python3 compatible
+# TODO:
+# Once python-augeas is available for python3 import the following things
+# from plinth.modules.pagekite.util (instead of having a copy in here):
+#
+# SERVICE_PARAMS, convert_service_to_string, convert_to_service
+#
+# until then, this file is python2 and python3 compatible for the unittests
import os
import shlex
diff --git a/plinth/__main__.py b/plinth/__main__.py
index 0c4a253a1..7ceacca80 100644
--- a/plinth/__main__.py
+++ b/plinth/__main__.py
@@ -222,9 +222,6 @@ def configure_django():
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'plinth.modules.first_boot.middleware.FirstBootMiddleware',
),
- MESSAGE_TAGS = {
- messages_constants.ERROR: 'danger'
- },
ROOT_URLCONF='plinth.urls',
SECURE_PROXY_SSL_HEADER=secure_proxy_ssl_header,
SESSION_ENGINE='django.contrib.sessions.backends.file',
diff --git a/plinth/modules/pagekite/__init__.py b/plinth/modules/pagekite/__init__.py
index b9ad5fb14..18fcccbcb 100644
--- a/plinth/modules/pagekite/__init__.py
+++ b/plinth/modules/pagekite/__init__.py
@@ -21,9 +21,8 @@ Plinth module to configure PageKite
from gettext import gettext as _
from plinth import cfg
-from . import pagekite
-__all__ = ['pagekite', 'init']
+__all__ = ['init']
depends = ['plinth.modules.apps']
diff --git a/plinth/modules/pagekite/forms.py b/plinth/modules/pagekite/forms.py
index 2df7f00c8..1228b9d5e 100644
--- a/plinth/modules/pagekite/forms.py
+++ b/plinth/modules/pagekite/forms.py
@@ -23,10 +23,9 @@ from django import forms
from django.contrib import messages
from django.core import validators
-from actions.pagekite_util import convert_service_to_string
from plinth.errors import ActionError
-from .util import PREDEFINED_SERVICES, _run, get_kite_details, KITE_NAME, \
- KITE_SECRET, BACKEND_HOST
+from .util import _run, convert_service_to_string, get_kite_details, \
+ BACKEND_HOST, KITE_NAME, KITE_SECRET, PREDEFINED_SERVICES
LOGGER = logging.getLogger(__name__)
diff --git a/plinth/modules/pagekite/pagekite.py b/plinth/modules/pagekite/pagekite.py
deleted file mode 100644
index a22c961d9..000000000
--- a/plinth/modules/pagekite/pagekite.py
+++ /dev/null
@@ -1,183 +0,0 @@
-#
-# This file is part of Plinth.
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see
PageKite is a system for exposing {{ cfg.box_name }} services when you don't have a direct connection to the Internet. You only need this -service if your {{ cfg.box_name }} services are unreachable from the +if your {{ cfg.box_name }} services are unreachable from the rest of the Internet. This includes the following situations:
PageKite works around NAT, firewalls and IP-address limitations by -using a combination of tunnels and reverse proxies. Currently, -exposing web server and SSH server are supported. You can use any -server that offers a pagekite service, for example +using a combination of tunnels and reverse proxies. You can use any +pagekite service provider, for example pagekite.net. -In future, it might be possible to use your buddy's +In future it might be possible to use your buddy's {{ cfg.box_name }} for this.
diff --git a/plinth/modules/pagekite/util.py b/plinth/modules/pagekite/util.py index 9fac5b859..d6c52b7dd 100644 --- a/plinth/modules/pagekite/util.py +++ b/plinth/modules/pagekite/util.py @@ -17,17 +17,20 @@ from gettext import gettext as _ import logging +import shlex -from actions.pagekite_util import convert_to_service from plinth import actions LOGGER = logging.getLogger(__name__) # defaults for the credentials; @kitename acts as a placeholder and is # understood (and replaced with the actual kitename) by pagekite. +BACKEND_HOST = 'localhost' KITE_NAME = '@kitename' KITE_SECRET = '@kitesecret' -BACKEND_HOST = 'localhost' + +SERVICE_PARAMS = ['protocol', 'kitename', 'backend_host', 'backend_port', + 'secret'] # Predefined services are used to build the PredefinedServiceForm # @@ -68,6 +71,57 @@ PREDEFINED_SERVICES = { } +def convert_to_service(service_string): + """ Convert a service string into a service parameter dictionary + >>> convert_to_service('https/443:@kitename:localhost:443:@kitesecret') + {'kitename': '@kitename', 'backend_host': 'localhost', \ +'secret': '@kitesecret', 'protocol': 'https/443', 'backend_port': '443'} + """ + # The actions.py uses shlex.quote() to escape/quote malicious user input. + # That affects '*.@kitename', so the params string gets quoted. + # If the string is escaped and contains '*.@kitename', look whether shlex + # would still quote/escape the string when we remove '*.@kitename'. + + if service_string.startswith("'") and service_string.endswith("'"): + unquoted_string = service_string[1:-1] + error_msg = "The parameters contain suspicious characters: %s " + if '*.@kitename' in service_string: + unquoted_test_string = unquoted_string.replace('*.@kitename', '') + if unquoted_test_string == shlex.quote(unquoted_test_string): + # no other malicious characters found, use the unquoted string + service_string = unquoted_string + else: + raise RuntimeError(error_msg % service_string) + else: + raise RuntimeError(error_msg % service_string) + + try: + params = dict(zip(SERVICE_PARAMS, service_string.split(':'))) + except Exception: + msg = """params are expected to be a ':'-separated string containing + values for: %s , for example:\n"--params + http/8000:@kitename:localhost:8000:@kitesecret" + """ + raise ValueError(msg % ", ".join(SERVICE_PARAMS)) + return params + + +def convert_service_to_string(service): + """ Convert service dict into a ":"-separated parameter string + + >>> convert_service_to_string({'kitename': '@kitename', \ +'backend_host': 'localhost', 'secret': '@kitesecret', \ +'protocol': 'https/443', 'backend_port': '443'}) + 'https/443:@kitename:localhost:443:@kitesecret' + """ + try: + service_string = ":".join([str(service[param]) for param in + SERVICE_PARAMS]) + except KeyError: + raise ValueError("Could not parse params: %s " % service) + return service_string + + def get_kite_details(): output = _run(['get-kite']) kite_details = output.split() diff --git a/plinth/modules/pagekite/views.py b/plinth/modules/pagekite/views.py index adfb0bcb5..8637b953f 100644 --- a/plinth/modules/pagekite/views.py +++ b/plinth/modules/pagekite/views.py @@ -30,6 +30,7 @@ from .forms import ConfigurationForm, DefaultServiceForm, \ AddCustomServiceForm, DeleteCustomServiceForm +required_packages = ('pagekite', 'augeas-tools', 'python-augeas') subsubmenu = [{'url': reverse_lazy('pagekite:index'), 'text': _('About PageKite')}, {'url': reverse_lazy('pagekite:configure'), @@ -50,7 +51,7 @@ def index(request): class ContextMixin(object): """Mixin to add 'subsubmenu' and 'title' to the context. - Also requires 'pagekite' to be installed. + Also adds the requirement of all necessary packages to be installed """ def get_context_data(self, **kwargs): """Use self.title and the module-level subsubmenu""" @@ -59,8 +60,7 @@ class ContextMixin(object): context['subsubmenu'] = subsubmenu return context - @method_decorator(package.required('pagekite', 'augeas-tools', - 'python-augeas')) + @method_decorator(package.required(required_packages)) def dispatch(self, *args, **kwargs): return super(ContextMixin, self).dispatch(*args, **kwargs) diff --git a/plinth/tests/test_pagekite_actions.py b/plinth/tests/test_pagekite_actions.py index 2c8d66b9e..c5904383e 100644 --- a/plinth/tests/test_pagekite_actions.py +++ b/plinth/tests/test_pagekite_actions.py @@ -18,12 +18,13 @@ import os import unittest -from actions.pagekite_util import get_augeas_servicefile_path, CONF_PATH, \ - convert_to_service, convert_service_to_string +from actions.pagekite_util import get_augeas_servicefile_path, CONF_PATH +from plinth.modules.pagekite.util import convert_to_service, \ + convert_service_to_string class TestPagekiteActions(unittest.TestCase): - # test-cases to convert parameter-strings into param dicts and back + """Test-cases for the pagekite action utils""" _tests = [ { 'line': 'https/8080:*.@kitename:localhost:8080:@kitesecret',