Merge pull request #1165 from paddatrapper/feature/python-force-ssl

Use force_ssl in python apps
This commit is contained in:
Lucas Bickel 2021-01-24 10:28:32 +01:00 committed by GitHub
commit f029584985
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 117 additions and 27 deletions

View File

@ -31,10 +31,8 @@ containers.
### Setup ### Setup
There are known bugs when using LibreTime behind a reverse proxy ([#957](https://github.com/LibreTime/libretime/issues/957) For SSL redirection to work, you need two domains: one for LibreTime and one for Icecast.
tracks the issue and contains a temporary workaround). For SSL redirection to work, you Here, these will be `libretime.example.com` and `icecast.example.com`.
need two domains: one for LibreTime and one for Icecast. Here, these will be
`libretime.example.com` and `icecast.example.com`.
You will also require two VMs, servers or containers. Alternatively the reverse proxy can You will also require two VMs, servers or containers. Alternatively the reverse proxy can
be located on the server, proxying connections to containers also on the host. Setting up be located on the server, proxying connections to containers also on the host. Setting up

View File

@ -10,7 +10,7 @@ import sys
import time import time
import urllib.request, urllib.error, urllib.parse import urllib.request, urllib.error, urllib.parse
import requests import requests
import socket import socket
import logging import logging
import json import json
import base64 import base64
@ -67,6 +67,17 @@ api_config['api_base'] = 'api'
api_config['bin_dir'] = '/usr/lib/airtime/api_clients/' api_config['bin_dir'] = '/usr/lib/airtime/api_clients/'
api_config['update_metadata_on_tunein'] = 'update-metadata-on-tunein/api_key/%%api_key%%' api_config['update_metadata_on_tunein'] = 'update-metadata-on-tunein/api_key/%%api_key%%'
def get_protocol(config):
positive_values = ['Yes', 'yes', 'True', 'true', True]
port = config['general'].get('base_port', 80)
force_ssl = config['general'].get('force_ssl', False)
if force_ssl in positive_values:
protocol = 'https'
else:
protocol = config['general'].get('protocol')
if not protocol:
protocol = str(("http", "https")[int(port) == 443])
return protocol
################################################################################ ################################################################################
@ -169,9 +180,11 @@ class RequestProvider(object):
self.requests = {} self.requests = {}
if self.config["general"]["base_dir"].startswith("/"): if self.config["general"]["base_dir"].startswith("/"):
self.config["general"]["base_dir"] = self.config["general"]["base_dir"][1:] self.config["general"]["base_dir"] = self.config["general"]["base_dir"][1:]
protocol = get_protocol(self.config)
self.url = ApcUrl("%s://%s:%s/%s%s/%s" \ self.url = ApcUrl("%s://%s:%s/%s%s/%s" \
% (str(("http", "https")[int(self.config["general"]["base_port"]) == 443]), % (protocol, self.config["general"]["base_url"],
self.config["general"]["base_url"], str(self.config["general"]["base_port"]), str(self.config["general"]["base_port"]),
self.config["general"]["base_dir"], self.config["api_base"], self.config["general"]["base_dir"], self.config["api_base"],
'%%action%%')) '%%action%%'))
# Now we must discover the possible actions # Now we must discover the possible actions
@ -208,7 +221,7 @@ class AirtimeApiClient(object):
def __get_airtime_version(self): def __get_airtime_version(self):
try: return self.services.version_url()['airtime_version'] try: return self.services.version_url()['airtime_version']
except Exception: return -1 except Exception: return -1
def __get_api_version(self): def __get_api_version(self):
try: return self.services.version_url()['api_version'] try: return self.services.version_url()['api_version']
except Exception: return -1 except Exception: return -1
@ -331,8 +344,9 @@ class AirtimeApiClient(object):
# TODO : Make other methods in this class use this this method. # TODO : Make other methods in this class use this this method.
if self.config["general"]["base_dir"].startswith("/"): if self.config["general"]["base_dir"].startswith("/"):
self.config["general"]["base_dir"] = self.config["general"]["base_dir"][1:] self.config["general"]["base_dir"] = self.config["general"]["base_dir"][1:]
protocol = get_protocol(self.config)
url = "%s://%s:%s/%s%s/%s" % \ url = "%s://%s:%s/%s%s/%s" % \
(str(("http", "https")[int(self.config["general"]["base_port"]) == 443]), (protocol,
self.config["general"]["base_url"], str(self.config["general"]["base_port"]), self.config["general"]["base_url"], str(self.config["general"]["base_port"]),
self.config["general"]["base_dir"], self.config["api_base"], self.config["general"]["base_dir"], self.config["api_base"],
self.config[config_action_key]) self.config[config_action_key])
@ -343,9 +357,9 @@ class AirtimeApiClient(object):
"""Constructs the base url for RESTful requests""" """Constructs the base url for RESTful requests"""
if self.config["general"]["base_dir"].startswith("/"): if self.config["general"]["base_dir"].startswith("/"):
self.config["general"]["base_dir"] = self.config["general"]["base_dir"][1:] self.config["general"]["base_dir"] = self.config["general"]["base_dir"][1:]
protocol = get_protocol(self.config)
url = "%s://%s:@%s:%s/%s/%s" % \ url = "%s://%s:@%s:%s/%s/%s" % \
(str(("http", "https")[int(self.config["general"]["base_port"]) == 443]), (protocol, self.config["general"]["api_key"],
self.config["general"]["api_key"],
self.config["general"]["base_url"], str(self.config["general"]["base_port"]), self.config["general"]["base_url"], str(self.config["general"]["base_port"]),
self.config["general"]["base_dir"], self.config["general"]["base_dir"],
self.config[config_action_key]) self.config[config_action_key])

View File

@ -5,7 +5,7 @@ class TestApcUrl(unittest.TestCase):
def test_init(self): def test_init(self):
url = "/testing" url = "/testing"
u = ApcUrl(url) u = ApcUrl(url)
self.assertEqual( u.base_url, url) self.assertEqual(u.base_url, url)
def test_params_1(self): def test_params_1(self):
u = ApcUrl("/testing/%%key%%") u = ApcUrl("/testing/%%key%%")

View File

@ -0,0 +1,75 @@
import unittest
import configparser
from api_clients.api_client import get_protocol
def get_force_ssl(value, useConfigParser):
config = {}
if useConfigParser:
config = configparser.ConfigParser()
config['general'] = {
'base_port': 80,
'force_ssl': value,
}
return get_protocol(config)
class TestGetProtocol(unittest.TestCase):
def test_dict_config_empty_http(self):
config = {'general': {}}
protocol = get_protocol(config)
self.assertEqual(protocol, 'http')
def test_dict_config_http(self):
config = {
'general': {
'base_port': 80,
},
}
protocol = get_protocol(config)
self.assertEqual(protocol, 'http')
def test_dict_config_https(self):
config = {
'general': {
'base_port': 443,
},
}
protocol = get_protocol(config)
self.assertEqual(protocol, 'https')
def test_dict_config_force_https(self):
postive_values = ['yes', 'Yes', 'True', 'true', True]
negative_values = ['no', 'No', 'False', 'false', False]
for value in postive_values:
self.assertEqual(get_force_ssl(value, False), 'https')
for value in negative_values:
self.assertEqual(get_force_ssl(value, False), 'http')
def test_configparser_config_empty_http(self):
config = configparser.ConfigParser()
config['general'] = {}
protocol = get_protocol(config)
self.assertEqual(protocol, 'http')
def test_configparser_config_http(self):
config = configparser.ConfigParser()
config['general'] = {
'base_port': 80,
}
protocol = get_protocol(config)
self.assertEqual(protocol, 'http')
def test_configparser_config_https(self):
config = configparser.ConfigParser()
config['general'] = {
'base_port': 443,
}
protocol = get_protocol(config)
self.assertEqual(protocol, 'https')
def test_configparser_config_force_https(self):
postive_values = ['yes', 'Yes', 'True', 'true', True]
negative_values = ['no', 'No', 'False', 'false', False]
for value in postive_values:
self.assertEqual(get_force_ssl(value, True), 'https')
for value in negative_values:
self.assertEqual(get_force_ssl(value, True), 'http')

View File

@ -13,11 +13,14 @@ class TestRequestProvider(unittest.TestCase):
self.cfg['general']['base_url'] = 'localhost' self.cfg['general']['base_url'] = 'localhost'
self.cfg['general']['api_key'] = 'TEST_KEY' self.cfg['general']['api_key'] = 'TEST_KEY'
self.cfg['api_base'] = 'api' self.cfg['api_base'] = 'api'
def test_test(self): def test_test(self):
self.assertTrue('general' in self.cfg) self.assertTrue('general' in self.cfg)
def test_init(self): def test_init(self):
rp = RequestProvider(self.cfg) rp = RequestProvider(self.cfg)
self.assertTrue( len( rp.available_requests() ) > 0 ) self.assertTrue( len( rp.available_requests() ) > 0 )
def test_contains(self): def test_contains(self):
rp = RequestProvider(self.cfg) rp = RequestProvider(self.cfg)
methods = ['upload_recorded', 'update_media_url', 'list_all_db_files'] methods = ['upload_recorded', 'update_media_url', 'list_all_db_files']

View File

@ -67,14 +67,14 @@ class PypoFile(Thread):
CONFIG_SECTION = "general" CONFIG_SECTION = "general"
username = self._config.get(CONFIG_SECTION, 'api_key') username = self._config.get(CONFIG_SECTION, 'api_key')
baseurl = self._config.get(CONFIG_SECTION, 'base_url') baseurl = self._config.get(CONFIG_SECTION, 'base_url')
try: port = self._config.get(CONFIG_SECTION, 'base_port', 80)
port = self._config.get(CONFIG_SECTION, 'base_port') if self._config.getboolean(CONFIG_SECTION, 'force_ssl', fallback=False):
except NoOptionError as e: protocol = 'https'
port = 80 else:
try: try:
protocol = self._config.get(CONFIG_SECTION, 'protocol') protocol = self._config.get(CONFIG_SECTION, 'protocol')
except NoOptionError as e: except NoOptionError as e:
protocol = str(("http", "https")[int(port) == 443]) protocol = str(("http", "https")[int(port) == 443])
try: try:
host = [protocol, baseurl, port] host = [protocol, baseurl, port]
@ -84,15 +84,15 @@ class PypoFile(Thread):
media_item["id"]) media_item["id"])
with open(dst, "wb") as handle: with open(dst, "wb") as handle:
response = requests.get(url, auth=requests.auth.HTTPBasicAuth(username, ''), stream=True, verify=False) response = requests.get(url, auth=requests.auth.HTTPBasicAuth(username, ''), stream=True, verify=False)
if not response.ok: if not response.ok:
self.logger.error(response) self.logger.error(response)
raise Exception("%s - Error occurred downloading file" % response.status_code) raise Exception("%s - Error occurred downloading file" % response.status_code)
for chunk in response.iter_content(1024): for chunk in response.iter_content(1024):
if not chunk: if not chunk:
break break
handle.write(chunk) handle.write(chunk)
#make file world readable and owner writable #make file world readable and owner writable
@ -160,11 +160,11 @@ class PypoFile(Thread):
""" """
Remove this media_item from the dictionary. On the next iteration Remove this media_item from the dictionary. On the next iteration
(from the main function) we won't consider it for prioritization (from the main function) we won't consider it for prioritization
anymore. If on the next iteration we have received a new schedule, anymore. If on the next iteration we have received a new schedule,
it is very possible we will have to deal with the same media_items it is very possible we will have to deal with the same media_items
again. In this situation, the worst possible case is that we try to again. In this situation, the worst possible case is that we try to
copy the file again and realize we already have it (thus aborting the copy). copy the file again and realize we already have it (thus aborting the copy).
""" """
del schedule[highest_priority] del schedule[highest_priority]
@ -179,7 +179,7 @@ class PypoFile(Thread):
logging.debug("Failed to open config file at %s: %s" % (config_path, e.strerror)) logging.debug("Failed to open config file at %s: %s" % (config_path, e.strerror))
sys.exit() sys.exit()
except Exception as e: except Exception as e:
logging.debug(e.strerror) logging.debug(e.strerror)
sys.exit() sys.exit()
return config return config