Support authentication tokens to construct networks

Closes #193
This commit is contained in:
Jace Browning 2017-01-31 15:00:38 -05:00
parent 7b3ab250df
commit 80f391761c
3 changed files with 36 additions and 10 deletions

2
.gitignore vendored
View file

@ -1,5 +1,6 @@
# User Credentials
test_pylast.yaml
.envrc
# Byte-compiled / optimized / DLL files
__pycache__/
@ -12,6 +13,7 @@ __pycache__/
# Distribution / packaging
.Python
env/
.venv/
build/
develop-eggs/
dist/

View file

@ -211,7 +211,8 @@ class _Network(object):
def __init__(
self, name, homepage, ws_server, api_key, api_secret, session_key,
submission_server, username, password_hash, domain_names, urls):
submission_server, username, password_hash, domain_names, urls,
token=None):
"""
name: the name of the network
homepage: the homepage URL
@ -227,6 +228,7 @@ class _Network(object):
domain_names: a dict mapping each DOMAIN_* value to a string domain
name
urls: a dict mapping types to URLs
token: an authentication token to retrieve a session
if username and password_hash were provided and not session_key,
session_key will be generated automatically when needed.
@ -257,6 +259,12 @@ class _Network(object):
self.last_call_time = 0
self.limit_rate = False
# Load session_key from authentication token if provided
if token and not self.session_key:
sk_gen = SessionKeyGenerator(self)
self.session_key = sk_gen.get_web_auth_session_key(
url=None, token=token)
# Generate a session_key if necessary
if ((self.api_key and self.api_secret) and not self.session_key and
(self.username and self.password_hash)):
@ -886,7 +894,7 @@ class LastFMNetwork(_Network):
def __init__(
self, api_key="", api_secret="", session_key="", username="",
password_hash=""):
password_hash="", token=""):
_Network.__init__(
self,
name="Last.fm",
@ -898,6 +906,7 @@ class LastFMNetwork(_Network):
submission_server="http://post.audioscrobbler.com:80/",
username=username,
password_hash=password_hash,
token=token,
domain_names={
DOMAIN_ENGLISH: 'www.last.fm',
DOMAIN_GERMAN: 'www.lastfm.de',
@ -936,7 +945,7 @@ class LastFMNetwork(_Network):
def get_lastfm_network(
api_key="", api_secret="", session_key="", username="",
password_hash=""):
password_hash="", token=""):
"""
Returns a preconfigured _Network object for Last.fm
@ -946,12 +955,13 @@ def get_lastfm_network(
username: a username of a valid user
password_hash: the output of pylast.md5(password) where password is the
user's password
token: an authentication token to retrieve a session
if username and password_hash were provided and not session_key,
session_key will be generated automatically when needed.
Either a valid session_key or a combination of username and password_hash
must be present for scrobbling.
Either a valid session_key, a combination of username and password_hash,
or token must be present for scrobbling.
Most read-only webservices only require an api_key and an api_secret, see
about obtaining them from:
@ -961,7 +971,7 @@ def get_lastfm_network(
_deprecation_warning("Create a LastFMNetwork object instead")
return LastFMNetwork(
api_key, api_secret, session_key, username, password_hash)
api_key, api_secret, session_key, username, password_hash, token)
class LibreFMNetwork(_Network):
@ -1304,7 +1314,7 @@ class SessionKeyGenerator(object):
return url
def get_web_auth_session_key(self, url):
def get_web_auth_session_key(self, url, token=""):
"""
Retrieves the session key of a web authorization process by its url.
"""
@ -1312,9 +1322,8 @@ class SessionKeyGenerator(object):
if url in self.web_auth_tokens.keys():
token = self.web_auth_tokens[url]
else:
# That's going to raise a WSError of an unauthorized token when the
# request is executed.
token = ""
# This will raise a WSError if token is blank or unauthorized
token = token
request = _Request(self.network, 'auth.getSession', {'token': token})
@ -1344,6 +1353,7 @@ class SessionKeyGenerator(object):
return _extract(doc, "key")
TopItem = collections.namedtuple("TopItem", ["item", "weight"])
SimilarItem = collections.namedtuple("SimilarItem", ["item", "match"])
LibraryItem = collections.namedtuple(

View file

@ -2160,6 +2160,20 @@ class TestPyLast(unittest.TestCase):
# Assert
self.assertEqual(mbid, None)
def test_init_with_token(self):
# Arrange/Act
try:
pylast.LastFMNetwork(
api_key=self.__class__.secrets["api_key"],
api_secret=self.__class__.secrets["api_secret"],
token="invalid",
)
except pylast.WSError as exc:
msg = str(exc)
# Assert
self.assertEqual(msg, "Invalid authentication token supplied")
@flaky(max_runs=5, min_passes=1)
class TestPyLastWithLibreFm(unittest.TestCase):