Merge pull request #117 from vimbaer/passwordeval

New general config option: passwordeval
This commit is contained in:
Markus Unterwaditzer 2014-09-13 14:29:54 +02:00
commit 3ad598c7b4
3 changed files with 63 additions and 5 deletions

View file

@ -10,6 +10,8 @@
import click
from click.testing import CliRunner
import os
import stat
import pytest
import requests
@ -108,6 +110,35 @@ def test_get_password_from_system_keyring(monkeypatch):
assert _password == password
def test_get_password_from_command(tmpdir):
username = 'my_username'
resource = 'http://example.com'
password = 'testpassword'
filename = 'command.sh'
filepath = str(tmpdir) + '/' + filename
f = open(filepath, 'w')
f.write('#!/bin/sh\n'
'[ "$1" != "my_username" ] && exit 1\n'
'[ "$2" != "example.com" ] && exit 1\n'
'echo "{}"'.format(password))
f.close()
st = os.stat(filepath)
os.chmod(filepath, st.st_mode | stat.S_IEXEC)
@doubleclick.click.command()
@doubleclick.click.pass_context
def fake_app(ctx):
ctx.obj = {'config' : ({'passwordeval' : filepath},{},{})}
_password = utils.get_password(username, resource)
assert _password == password
runner = CliRunner()
result = runner.invoke(fake_app)
assert not result.exception
def test_get_password_from_prompt():
getpass_calls = []
@ -187,7 +218,6 @@ def test_get_password_from_cache(monkeypatch):
]
def test_get_class_init_args():
class Foobar(object):
def __init__(self, foo, bar, baz=None):

View file

@ -30,7 +30,7 @@ cli_logger = log.get(__name__)
PROJECT_HOME = 'https://github.com/untitaker/vdirsyncer'
DOCS_HOME = 'https://vdirsyncer.readthedocs.org/en/latest'
GENERAL_ALL = set(['processes', 'status_path'])
GENERAL_ALL = set(['processes', 'status_path', 'passwordeval'])
GENERAL_REQUIRED = set(['status_path'])

View file

@ -97,7 +97,9 @@ def get_password(username, resource, _lock=threading.Lock()):
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
3. read password from the command passed as passwordeval in the
general config section with username and host as parameters
4a ask user for the password
b save in keyring if installed and user agrees
:param username: user's name on the server
@ -117,8 +119,8 @@ def get_password(username, resource, _lock=threading.Lock()):
with _lock:
host = urlparse.urlsplit(resource).hostname
for func in (_password_from_cache, _password_from_netrc,
_password_from_keyring):
for func in (_password_from_command, _password_from_cache,
_password_from_netrc, _password_from_keyring):
password = func(username, host)
if password is not None:
logger.debug('Got password for {} from {}'
@ -166,6 +168,32 @@ def _password_from_keyring(username, host):
return keyring.get_password(password_key_prefix + host, username)
def _password_from_command(username, host):
'''command'''
import subprocess
try:
general, _, _ = ctx.obj['config']
_command = general['passwordeval'].split()
except (IndexError, KeyError):
return None
command = [expand_path(_command[0])]
if len(_command) > 1:
command += _command[1:]
try:
proc = subprocess.Popen(command + [username, host],
stdout=subprocess.PIPE)
password = proc.stdout.read().decode('utf-8').strip()
except OSError as e:
logger.debug('Failed to execute command: {}\n{}'.
format(" ".join(command), str(e)))
return None
return password
class _FingerprintAdapter(requests.adapters.HTTPAdapter):
def __init__(self, fingerprint=None, **kwargs):
self.fingerprint = str(fingerprint)