mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-03-25 08:55:50 +00:00
Added passwordeval as an option for the general config section. If no password is provided the command provided as passwordeval will be called with username and hostname as arguments.
This commit is contained in:
parent
9dbb359569
commit
4e895b8635
3 changed files with 64 additions and 4 deletions
|
|
@ -10,6 +10,9 @@
|
|||
import click
|
||||
|
||||
from click.testing import CliRunner
|
||||
from tempfile import mkstemp
|
||||
import os
|
||||
import stat
|
||||
import pytest
|
||||
import requests
|
||||
|
||||
|
|
@ -108,6 +111,35 @@ def test_get_password_from_system_keyring(monkeypatch):
|
|||
assert _password == password
|
||||
|
||||
|
||||
def test_get_password_from_evalcmd():
|
||||
username = 'my_username'
|
||||
resource = 'http://example.com'
|
||||
password = 'testpassword'
|
||||
|
||||
fd, temp_path = mkstemp()
|
||||
os.close(fd)
|
||||
fp = open(temp_path, 'w')
|
||||
fp.write('#!/bin/sh\n'
|
||||
'[ "$1" != "my_username" ] && exit 1\n'
|
||||
'[ "$2" != "example.com" ] && exit 1\n'
|
||||
'echo "{}"'.format(password))
|
||||
fp.close()
|
||||
st = os.stat(temp_path)
|
||||
os.chmod(temp_path, st.st_mode | stat.S_IEXEC)
|
||||
|
||||
@doubleclick.click.command()
|
||||
@doubleclick.click.pass_context
|
||||
def fake_app(ctx):
|
||||
ctx.obj = {'config' : ({'passwordeval' : temp_path},{},{})}
|
||||
_password = utils.get_password(username, resource)
|
||||
assert _password == password
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(fake_app)
|
||||
assert not result.exception
|
||||
os.remove(temp_path)
|
||||
|
||||
|
||||
def test_get_password_from_prompt():
|
||||
getpass_calls = []
|
||||
|
||||
|
|
@ -187,7 +219,6 @@ def test_get_password_from_cache(monkeypatch):
|
|||
]
|
||||
|
||||
|
||||
|
||||
def test_get_class_init_args():
|
||||
class Foobar(object):
|
||||
def __init__(self, foo, bar, baz=None):
|
||||
|
|
|
|||
|
|
@ -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'])
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -118,7 +120,7 @@ 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):
|
||||
_password_from_keyring, _password_from_evalcmd):
|
||||
password = func(username, host)
|
||||
if password is not None:
|
||||
logger.debug('Got password for {} from {}'
|
||||
|
|
@ -166,6 +168,33 @@ def _password_from_keyring(username, host):
|
|||
return keyring.get_password(password_key_prefix + host, username)
|
||||
|
||||
|
||||
def _password_from_evalcmd(username, host):
|
||||
'''evalcmd'''
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
general, _, _ = ctx.obj['config']
|
||||
_evalcmd = general.get('passwordeval', '')
|
||||
except (KeyError, IndexError):
|
||||
return None
|
||||
|
||||
if _evalcmd == '':
|
||||
return None
|
||||
|
||||
evalcmd = expand_path(_evalcmd)
|
||||
|
||||
try:
|
||||
proc = subprocess.Popen([evalcmd, username, host],
|
||||
stdout=subprocess.PIPE)
|
||||
password = proc.stdout.read().strip()
|
||||
except OSError, e:
|
||||
logger.debug('Failed to execute evalcmd: {}\n{}'.
|
||||
format(evalcmd, str(e)))
|
||||
return None
|
||||
|
||||
return password
|
||||
|
||||
|
||||
class _FingerprintAdapter(requests.adapters.HTTPAdapter):
|
||||
def __init__(self, fingerprint=None, **kwargs):
|
||||
self.fingerprint = str(fingerprint)
|
||||
|
|
|
|||
Loading…
Reference in a new issue