Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use endwith() instead of endWith() #3598

Merged
merged 3 commits into from
May 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 43 additions & 43 deletions beetsplug/subsonicupdate.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,47 +35,6 @@
__author__ = 'https://github.com/maffo999'


def create_token():
"""Create salt and token from given password.

:return: The generated salt and hashed token
"""
password = config['subsonic']['pass'].as_str()

# Pick the random sequence and salt the password
r = string.ascii_letters + string.digits
salt = "".join([random.choice(r) for _ in range(6)])
salted_password = password + salt
token = hashlib.md5().update(salted_password.encode('utf-8')).hexdigest()

# Put together the payload of the request to the server and the URL
return salt, token


def format_url():
"""Get the Subsonic URL to trigger a scan. Uses either the url
config option or the deprecated host, port, and context_path config
options together.

:return: Endpoint for updating Subsonic
"""

url = config['subsonic']['url'].as_str()
if url and url.endsWith('/'):
url = url[:-1]

# @deprecated("Use url config option instead")
if not url:
host = config['subsonic']['host'].as_str()
port = config['subsonic']['port'].get(int)
context_path = config['subsonic']['contextpath'].as_str()
if context_path == '/':
context_path = ''
url = "http://{}:{}{}".format(host, port, context_path)

return url + '/rest/startScan'


class SubsonicUpdate(BeetsPlugin):
def __init__(self):
super(SubsonicUpdate, self).__init__()
Expand All @@ -90,10 +49,51 @@ def __init__(self):
config['subsonic']['pass'].redact = True
self.register_listener('import', self.start_scan)

@staticmethod
def __create_token():
"""Create salt and token from given password.

:return: The generated salt and hashed token
"""
password = config['subsonic']['pass'].as_str()

# Pick the random sequence and salt the password
r = string.ascii_letters + string.digits
salt = "".join([random.choice(r) for _ in range(6)])
salted_password = password + salt
token = hashlib.md5(salted_password.encode('utf-8')).hexdigest()
Copy link
Member Author

@jef jef May 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also changed this, but hard to tell in GitHub diff...

Used to be hashlib.md5().update(salted_password.encode('utf-8')).hexdigest(). Seems like a better use of hashlib.


# Put together the payload of the request to the server and the URL
return salt, token

@staticmethod
def __format_url():
"""Get the Subsonic URL to trigger a scan. Uses either the url
config option or the deprecated host, port, and context_path config
options together.

:return: Endpoint for updating Subsonic
"""

url = config['subsonic']['url'].as_str()
if url and url.endswith('/'):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the main problem.

url = url[:-1]

# @deprecated("Use url config option instead")
if not url:
host = config['subsonic']['host'].as_str()
port = config['subsonic']['port'].get(int)
context_path = config['subsonic']['contextpath'].as_str()
if context_path == '/':
context_path = ''
url = "http://{}:{}{}".format(host, port, context_path)

return url + '/rest/startScan'

def start_scan(self):
user = config['subsonic']['user'].as_str()
url = format_url()
salt, token = create_token()
url = self.__format_url()
salt, token = self.__create_token()

payload = {
'u': user,
Expand Down
111 changes: 111 additions & 0 deletions test/test_subsonic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-

"""Tests for the 'subsonic' plugin"""

from __future__ import division, absolute_import, print_function

import requests
import responses
import unittest

from test import _common
from beets import config
from beetsplug import subsonicupdate
from test.helper import TestHelper
from six.moves.urllib.parse import parse_qs, urlparse


class ArgumentsMock(object):
def __init__(self, mode, show_failures):
self.mode = mode
self.show_failures = show_failures
self.verbose = 1


def _params(url):
"""Get the query parameters from a URL."""
return parse_qs(urlparse(url).query)


class SubsonicPluginTest(_common.TestCase, TestHelper):
@responses.activate
def setUp(self):
config.clear()
self.setup_beets()

config["subsonic"]["user"] = "admin"
config["subsonic"]["pass"] = "admin"
config["subsonic"]["url"] = "http://localhost:4040"

self.subsonicupdate = subsonicupdate.SubsonicUpdate()

def tearDown(self):
self.teardown_beets()

@responses.activate
def test_start_scan(self):
responses.add(
responses.POST,
'http://localhost:4040/rest/startScan',
status=200
)

self.subsonicupdate.start_scan()

@responses.activate
def test_url_with_extra_forward_slash_url(self):
config["subsonic"]["url"] = "http://localhost:4040/contextPath"

responses.add(
responses.POST,
'http://localhost:4040/contextPath/rest/startScan',
status=200
)

self.subsonicupdate.start_scan()

@responses.activate
def test_url_with_context_path(self):
config["subsonic"]["url"] = "http://localhost:4040/"

responses.add(
responses.POST,
'http://localhost:4040/rest/startScan',
status=200
)

self.subsonicupdate.start_scan()

@responses.activate
def test_url_with_missing_port(self):
config["subsonic"]["url"] = "http://localhost/airsonic"

responses.add(
responses.POST,
'http://localhost:4040/rest/startScan',
status=200
)

with self.assertRaises(requests.exceptions.ConnectionError):
self.subsonicupdate.start_scan()

@responses.activate
def test_url_with_missing_schema(self):
config["subsonic"]["url"] = "localhost:4040/airsonic"

responses.add(
responses.POST,
'http://localhost:4040/rest/startScan',
status=200
)

with self.assertRaises(requests.exceptions.InvalidSchema):
self.subsonicupdate.start_scan()


def suite():
return unittest.TestLoader().loadTestsFromName(__name__)


if __name__ == '__main__':
unittest.main(defaultTest='suite')