From 28213d3475aaf753f164bd9b4fd029c518435abf Mon Sep 17 00:00:00 2001 From: Mark Lee Date: Sat, 14 Jun 2014 13:58:37 -0700 Subject: [PATCH] Use the keyring package for password storage Fixes GH malept/gmusicprocurator#11. --- INSTALL.rst | 20 +++++++++++++++----- bower.json | 2 +- docs/config.rst | 6 ------ gmusicprocurator/__main__.py | 35 ++++++++++++++++++++++++++++++++--- gmusicprocurator/app.py | 9 +++++++-- requirements/base.txt | 1 + 6 files changed, 56 insertions(+), 17 deletions(-) diff --git a/INSTALL.rst b/INSTALL.rst index 842f7f3..0a21aeb 100644 --- a/INSTALL.rst +++ b/INSTALL.rst @@ -53,7 +53,6 @@ The contents of the file will look like this: .. code-block:: python GACCOUNT_EMAIL = 'my-google-account@gmail.com' - GACCOUNT_PASSWORD = 'my-password' Then run the following (lines that start with ``#`` are comments, not commands): @@ -69,12 +68,23 @@ Then run the following (lines that start with ``#`` are comments, not commands): # Only run the next line if you wish to use libsass-python instead of the # Ruby version of Sass: (venv)user@host:gmusicprocurator$ pip install libsass + (venv)user@host:gmusicprocurator$ python -m gmusicprocurator set_password + +The last command will activate an interactive prompt that will store your +Google account password (or, if your account has two-factor authentication +enabled, your application-specific password) into the operating system's +password storage service. + +Once your password is set, you will need to associate GMusicProcurator with one +of your mobile devices. Run the following command to list the devices: + +.. code-block:: shell-session + (venv)user@host:gmusicprocurator$ python -m gmusicprocurator list_devices --no-desktop -The last command will print out a list of mobile devices that are registered -with Google Music. Select one of them and add the following to the config file -from above (substituting ``REPLACE_ME`` with the ID, which is after the colon -in the device ID printout): +Select one of them and add the following to the config file from above +(substituting ``REPLACE_ME`` with the ID, which is after the colon in the +device ID printout): .. code-block:: python diff --git a/bower.json b/bower.json index 7f0754c..3c70177 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "gmusicprocurator", - "version": "1.0dev3", + "version": "1.0dev4", "homepage": "https://gmusicprocurator.readthedocs.org/", "authors": [ "Mark Lee" diff --git a/docs/config.rst b/docs/config.rst index 1943f05..1977faa 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -11,12 +11,6 @@ Google The Google Account email address that has access to Google Music. -.. attribute:: GACCOUNT_PASSWORD - - The Google Account password associated with the email address specified in - :attr:`GACCOUNT_EMAIL`. If you use two-factor authentiation, this is an - app-specific password. - .. attribute:: GACCOUNT_DEVICE_ID The mobile device ID to use to access Google Music. See :doc:`the diff --git a/gmusicprocurator/__main__.py b/gmusicprocurator/__main__.py index a3119ab..595d1fc 100644 --- a/gmusicprocurator/__main__.py +++ b/gmusicprocurator/__main__.py @@ -21,6 +21,7 @@ from flask.ext.script import Manager from functools import partial +from getpass import getpass from gmusicprocurator.app import app, heapy import sys @@ -44,6 +45,11 @@ def dump_memory_usage(signum, frame): from gmusicprocurator.app import assets manager.add_command('assets', ManageAssets(assets)) + +def error(msg): + """Print an error message (prepended by ``ERROR: ``) to stderr.""" + print('ERROR: {0}'.format(msg), file=sys.stderr) + no_bool_option = partial(manager.option, action='store_false', default=True) @@ -56,11 +62,16 @@ def list_devices(show_desktop, show_mobile): Defaults to showing both desktop and mobile IDs. """ from gmusicapi.clients import Webclient + from keyring import get_password webclient = Webclient() - success = webclient.login(app.config['GACCOUNT_EMAIL'], - app.config['GACCOUNT_PASSWORD']) + email = app.config['GACCOUNT_EMAIL'] + password = get_password('gmusicprocurator', email) + if password is None: + error('Password not set. Please run the set_password subcommand.') + return + success = webclient.login(email, password) if not success: - print('Login failed.', file=sys.stderr) + error('Login failed.') return for device in webclient.get_registered_devices(): @@ -72,6 +83,24 @@ def list_devices(show_desktop, show_mobile): print(u'* {dname} ({type}): {id}'.format(dname=dname, **device)) +@manager.command +def set_password(): + """Set the Google account password.""" + import keyring + password = None + repeated = None + while password is None or password != repeated: + password = getpass('Please enter your password: ') + repeated = getpass('Please verify your password: ') + if password == repeated: + keyring.set_password('gmusicprocurator', + app.config['GACCOUNT_EMAIL'], + password) + print('Password set successfully.') + else: + error('Passwords do not match.') + + def run(): """Flask-Script convenience runner.""" manager.run() diff --git a/gmusicprocurator/app.py b/gmusicprocurator/app.py index b209d01..8fd1d33 100644 --- a/gmusicprocurator/app.py +++ b/gmusicprocurator/app.py @@ -39,9 +39,14 @@ music = None else: from gmusicapi import Mobileclient + from keyring import get_password music = Mobileclient() - music.login(app.config['GACCOUNT_EMAIL'], - app.config['GACCOUNT_PASSWORD']) + email = app.config['GACCOUNT_EMAIL'] + password = get_password('gmusicprocurator', email) + if password is None: + music = None + else: + music.login(email, password) if app.debug and app.config['GMP_MEMORY_PROFILER']: from guppy import hpy diff --git a/requirements/base.txt b/requirements/base.txt index 50ecf46..8a34015 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,6 @@ Flask Flask-Script gmusicapi +keyring # xspf git+https://github.com/alastair/xspf.git#egg=xspf