From 2b1977892e2f269aa29bb40ed4826d978bebe454 Mon Sep 17 00:00:00 2001 From: spl0k Date: Sun, 1 Apr 2018 12:32:36 +0200 Subject: [PATCH 1/4] Generate a secret key only once --- supysonic/config.py | 1 - supysonic/web.py | 20 ++++++++++++---- tests/base/__init__.py | 2 ++ tests/base/test_secret.py | 48 +++++++++++++++++++++++++++++++++++++++ tests/testbase.py | 1 - 5 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 tests/base/test_secret.py diff --git a/supysonic/config.py b/supysonic/config.py index 0e602b8..f82487c 100644 --- a/supysonic/config.py +++ b/supysonic/config.py @@ -18,7 +18,6 @@ import tempfile class DefaultConfig(object): DEBUG = False - SECRET_KEY = os.urandom(128) tempdir = os.path.join(tempfile.gettempdir(), 'supysonic') BASE = { diff --git a/supysonic/web.py b/supysonic/web.py index ac5259d..1af92d8 100644 --- a/supysonic/web.py +++ b/supysonic/web.py @@ -8,10 +8,11 @@ # # Distributed under terms of the GNU AGPLv3 license. +import io import mimetypes from flask import Flask -from os import makedirs, path +from os import makedirs, path, urandom from pony.orm import db_session from .config import IniConfig @@ -24,13 +25,13 @@ def create_application(config = None): app = Flask(__name__) app.config.from_object('supysonic.config.DefaultConfig') - if not config: + if not config: # pragma: nocover config = IniConfig.from_common_locations() app.config.from_object(config) # Set loglevel logfile = app.config['WEBAPP']['log_file'] - if logfile: + if logfile: # pragma: nocover import logging from logging.handlers import TimedRotatingFileHandler handler = TimedRotatingFileHandler(logfile, when = 'midnight') @@ -59,7 +60,18 @@ def create_application(config = None): # Test for the cache directory cache_path = app.config['WEBAPP']['cache_dir'] if not path.exists(cache_path): - makedirs(cache_path) + makedirs(cache_path) # pragma: nocover + + # Read or create secret key + secret_path = path.join(cache_path, 'secret') + if path.exists(secret_path): + with io.open(secret_path, 'rb') as f: + app.secret_key = f.read() + else: + secret = urandom(128) + with io.open(secret_path, 'wb') as f: + f.write(secret) + app.secret_key = secret # Import app sections if app.config['WEBAPP']['mount_webui']: diff --git a/tests/base/__init__.py b/tests/base/__init__.py index 30098d4..2f4630e 100644 --- a/tests/base/__init__.py +++ b/tests/base/__init__.py @@ -14,6 +14,7 @@ from .test_config import ConfigTestCase from .test_db import DbTestCase from .test_lastfm import LastFmTestCase from .test_scanner import ScannerTestCase +from .test_secret import SecretTestCase from .test_watcher import suite as watcher_suite def suite(): @@ -25,6 +26,7 @@ def suite(): suite.addTest(watcher_suite()) suite.addTest(unittest.makeSuite(CLITestCase)) suite.addTest(unittest.makeSuite(LastFmTestCase)) + suite.addTest(unittest.makeSuite(SecretTestCase)) return suite diff --git a/tests/base/test_secret.py b/tests/base/test_secret.py new file mode 100644 index 0000000..8f186cc --- /dev/null +++ b/tests/base/test_secret.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# coding: utf-8 +# +# This file is part of Supysonic. +# Supysonic is a Python implementation of the Subsonic server API. +# +# Copyright (C) 2018 Alban 'spl0k' FĂ©ron +# +# Distributed under terms of the GNU AGPLv3 license. + +import os +import unittest +import shutil +import tempfile + +from supysonic.db import init_database, release_database +from supysonic.web import create_application + +from ..testbase import TestConfig + +class SecretTestCase(unittest.TestCase): + def setUp(self): + self.__dbfile = tempfile.mkstemp()[1] + self.__dir = tempfile.mkdtemp() + self.config = TestConfig(False, False) + self.config.BASE['database_uri'] = 'sqlite:///' + self.__dbfile + self.config.WEBAPP['cache_dir'] = self.__dir + + init_database(self.config.BASE['database_uri'], True) + release_database() + + def tearDown(self): + shutil.rmtree(self.__dir) + os.remove(self.__dbfile) + + + def test_key(self): + app1 = create_application(self.config) + release_database() + + app2 = create_application(self.config) + release_database() + + self.assertEqual(app1.secret_key, app2.secret_key) + +if __name__ == '__main__': + unittest.main() + diff --git a/tests/testbase.py b/tests/testbase.py index d93b477..8d3dad8 100644 --- a/tests/testbase.py +++ b/tests/testbase.py @@ -8,7 +8,6 @@ # Distributed under terms of the GNU AGPLv3 license. import inspect -import io import os import shutil import unittest From 66bc65a46c9bd58fefc82243a8fbda9cccb8b286 Mon Sep 17 00:00:00 2001 From: spl0k Date: Sat, 1 Sep 2018 17:52:00 +0200 Subject: [PATCH 2/4] Fixed setup installing tests --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ca97a79..3b46027 100755 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ setup( author_email=project.AUTHOR_EMAIL, url=project.URL, license=project.LICENSE, - packages=find_packages(), + packages=find_packages(exclude=['tests*']), install_requires = reqs, extras_require = extras, scripts=['bin/supysonic-cli', 'bin/supysonic-watcher'], From 6f6521786a90ce669752f7edf79990067f864730 Mon Sep 17 00:00:00 2001 From: spl0k Date: Sat, 8 Sep 2018 15:22:28 +0200 Subject: [PATCH 3/4] Updated README for Apache2 --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 715f48c..eefbacc 100644 --- a/README.md +++ b/README.md @@ -166,8 +166,7 @@ example of what it looks like: WSGIApplicationGroup %{GLOBAL} WSGIPassAuthorization On - Order deny,allow - Allow from all + Require all granted You might also need to run _Apache_ using the system default locale, as the one From 59f7fd62b04e5cc911790602f0cc3d168c5cdfe4 Mon Sep 17 00:00:00 2001 From: spl0k Date: Sat, 15 Sep 2018 16:02:36 +0200 Subject: [PATCH 4/4] Improved transcoding description and added client-side validation Closes #115 --- supysonic/templates/profile.html | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/supysonic/templates/profile.html b/supysonic/templates/profile.html index 4dc3cab..0713853 100644 --- a/supysonic/templates/profile.html +++ b/supysonic/templates/profile.html @@ -93,10 +93,18 @@

Clients

Here's a list of clients you used to stream music. If you want to use -transcoding or downsampling with one of them (for instance using a low -bitrate on mobile connections to reduce used bandwidth), but the client -doesn't provide options to do so, you can set default values here. They'll -only be used if no transcoding/downsampling is requested by the client.

+transcoding or downsampling with one of them (for instance using a low bitrate +on mobile connections to reduce used bandwidth), but the client doesn't provide +options to do so, you can set default values here. They'll only be used if no +transcoding/downsampling is requested by the client.
+The first field is used to specify the format all files will be converted to. It +means if you enter mp3, all media will be served as mp3, wether the +original file is a mp3, ogg, FLAC or whatever. The bitrate option specify the +maximum streaming bitrate. If a file has a higher bitrate it will be transcoded +to match this value. Note that either the format or bitrate field can be left +out, for instance you can only enter a bitrate so files will be streamed using +their original format, only transcoded if their bitrate exceed the selected +one.

@@ -106,7 +114,9 @@ only be used if no transcoding/downsampling is requested by the client.

{% for client in clients %} - +