From 2a2e4dd04d95da8d7d7d6c6bf5434599100ccd30 Mon Sep 17 00:00:00 2001 From: Christian Geier Date: Wed, 26 Mar 2014 18:52:16 +0100 Subject: [PATCH] system password storages should fix github issue #14 --- setup.py | 3 +- vdirsyncer/storage/http.py | 5 ++- vdirsyncer/utils.py | 76 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index a18ba72..838ba49 100644 --- a/setup.py +++ b/setup.py @@ -25,5 +25,6 @@ setup( entry_points={ 'console_scripts': ['vdirsyncer = vdirsyncer.cli:main'] }, - install_requires=['argvard', 'requests', 'lxml'] + install_requires=['argvard', 'requests', 'lxml'], + extras_require={'keyring': ['keyring']} ) diff --git a/vdirsyncer/storage/http.py b/vdirsyncer/storage/http.py index 0d0eded..98109b2 100644 --- a/vdirsyncer/storage/http.py +++ b/vdirsyncer/storage/http.py @@ -12,7 +12,7 @@ import requests import urlparse import hashlib from .base import Storage, Item -from vdirsyncer.utils import expand_path +from vdirsyncer.utils import expand_path, get_password def split_collection(text): @@ -81,6 +81,9 @@ class HttpStorageBase(Storage): ''' super(HttpStorageBase, self).__init__(**kwargs) + if username and not password: + password = get_password(username, url) + self._settings = { 'verify': prepare_verify(verify), 'auth': prepare_auth(auth, username, password) diff --git a/vdirsyncer/utils.py b/vdirsyncer/utils.py index 152eae1..648d4eb 100644 --- a/vdirsyncer/utils.py +++ b/vdirsyncer/utils.py @@ -9,6 +9,7 @@ import os +import vdirsyncer.log def expand_path(p): p = os.path.expanduser(p) @@ -39,3 +40,78 @@ def parse_options(items): except ValueError: pass yield key, value + + +def get_password(username, resource): + """tries to access saved password or asks user for it + + will try the following in this order: + 1. read password from netrc (and only the password, username + in netrc will be ignored) + 2. read password from keyring (keyring needs to be installed) + 3a ask user for the password + b save in keyring if installed and user agrees + + :param username: user's name on the server + :type username: str/unicode + :param resource: a resource to which the user has access via password, + it will be shortened to just the hostname. It is assumed + that each unique username/hostname combination only ever + uses the same password. + :type resource: str/unicode + :return: password + :rtype: str/unicode + + + """ + import getpass + from netrc import netrc + try: + from urlparse import urlsplit + except ImportError: + from urllib.parse import urlsplit + + sync_logger = vdirsyncer.log.get('sync') + + # XXX is it save to asume that a password is always the same for + # any given (hostname, username) combination? + hostname = urlsplit(resource).hostname + + # netrc + try: + auths = netrc().authenticators(hostname) + # auths = (user, password) + except IOError: + pass + else: + sync_logger.debug("Read password for user {0} on {1} in .netrc".format( + auths[0], hostname)) + return auths[1] + + # keyring + try: + import keyring + except ImportError: + keyring = None + else: + password = keyring.get_password( + 'vdirsyncer:' + hostname, username) + if password is not None: + sync_logger.debug("Got password for user {0}@{1} from keyring".format( + username, hostname)) + return password + + if password is None: + prompt = 'Server password {0}@{1}: '.format(username, hostname) + password = getpass.getpass(prompt=prompt) + + if keyring: + answer = 'x' + while answer.lower() not in ['', 'y', 'n']: + prompt = 'Save this password in the keyring? [y/N] ' + answer = raw_input(prompt) + if answer.lower() == 'y': + password = keyring.set_password( + 'vdirsyncer:' + hostname, username, password) + + return password