mirror of
https://github.com/samsonjs/vdirsyncer.git
synced 2026-03-25 08:55:50 +00:00
263 lines
7.7 KiB
Python
263 lines
7.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import logging
|
|
import os
|
|
import platform
|
|
import stat
|
|
|
|
import click
|
|
from click.testing import CliRunner
|
|
|
|
import click_log
|
|
|
|
import pytest
|
|
|
|
import requests
|
|
|
|
from vdirsyncer import utils
|
|
from vdirsyncer.cli import pass_context
|
|
|
|
# These modules might be uninitialized and unavailable if not explicitly
|
|
# imported
|
|
import vdirsyncer.utils.compat # noqa
|
|
import vdirsyncer.utils.http # noqa
|
|
import vdirsyncer.utils.password # noqa
|
|
|
|
|
|
from .. import blow_up
|
|
|
|
|
|
class EmptyNetrc(object):
|
|
def __init__(self, file=None):
|
|
self._file = file
|
|
|
|
def authenticators(self, hostname):
|
|
return None
|
|
|
|
|
|
class EmptyKeyring(object):
|
|
def get_password(self, *a, **kw):
|
|
return None
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def empty_password_storages(monkeypatch):
|
|
monkeypatch.setattr('netrc.netrc', EmptyNetrc)
|
|
monkeypatch.setattr(utils.password, 'keyring', EmptyKeyring())
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def no_debug_output(request):
|
|
logger = click_log.basic_config('vdirsyncer')
|
|
logger.setLevel(logging.WARNING)
|
|
old = logger.level
|
|
|
|
def teardown():
|
|
logger.setLevel(old)
|
|
|
|
request.addfinalizer(teardown)
|
|
|
|
|
|
def test_get_password_from_netrc(monkeypatch):
|
|
username = 'foouser'
|
|
password = 'foopass'
|
|
resource = 'http://example.com/path/to/whatever/'
|
|
hostname = 'example.com'
|
|
|
|
calls = []
|
|
|
|
class Netrc(object):
|
|
def authenticators(self, hostname):
|
|
calls.append(hostname)
|
|
return username, 'bogus', password
|
|
|
|
monkeypatch.setattr('netrc.netrc', Netrc)
|
|
monkeypatch.setattr('getpass.getpass', blow_up)
|
|
|
|
_password = utils.password.get_password(username, resource)
|
|
assert _password == password
|
|
assert calls == [hostname]
|
|
|
|
|
|
def test_get_password_from_system_keyring(monkeypatch):
|
|
username = 'foouser'
|
|
password = 'foopass'
|
|
resource = 'http://example.com/path/to/whatever/'
|
|
hostname = 'example.com'
|
|
|
|
class KeyringMock(object):
|
|
def get_password(self, resource, _username):
|
|
assert _username == username
|
|
assert resource == utils.password.password_key_prefix + hostname
|
|
return password
|
|
|
|
monkeypatch.setattr(utils.password, 'keyring', KeyringMock())
|
|
|
|
monkeypatch.setattr('getpass.getpass', blow_up)
|
|
|
|
_password = utils.password.get_password(username, resource)
|
|
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)
|
|
|
|
@click.command()
|
|
@pass_context
|
|
def fake_app(ctx):
|
|
ctx.config = {'password_command': filepath}, {}, {}
|
|
_password = utils.password.get_password(username, resource)
|
|
assert _password == password
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(fake_app)
|
|
assert not result.exception
|
|
|
|
|
|
def test_get_password_from_prompt():
|
|
user = 'my_user'
|
|
resource = 'http://example.com'
|
|
|
|
@click.command()
|
|
def fake_app():
|
|
x = utils.password.get_password(user, resource)
|
|
click.echo('Password is {}'.format(x))
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(fake_app, input='my_password\n\n')
|
|
assert not result.exception
|
|
assert result.output.splitlines() == [
|
|
'Server password for my_user at host example.com: ',
|
|
'Save this password in the keyring? [y/N]: ',
|
|
'Password is my_password',
|
|
]
|
|
|
|
|
|
def test_set_keyring_password(monkeypatch):
|
|
class KeyringMock(object):
|
|
def get_password(self, resource, username):
|
|
assert resource == \
|
|
utils.password.password_key_prefix + 'example.com'
|
|
assert username == 'foouser'
|
|
return None
|
|
|
|
def set_password(self, resource, username, password):
|
|
assert resource == \
|
|
utils.password.password_key_prefix + 'example.com'
|
|
assert username == 'foouser'
|
|
assert password == 'hunter2'
|
|
|
|
monkeypatch.setattr(utils.password, 'keyring', KeyringMock())
|
|
|
|
@click.command()
|
|
@pass_context
|
|
def fake_app(ctx):
|
|
x = utils.password.get_password('foouser', 'http://example.com/a/b')
|
|
click.echo('password is ' + x)
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(fake_app, input='hunter2\ny\n')
|
|
assert not result.exception
|
|
assert result.output == (
|
|
'Server password for foouser at host example.com: \n'
|
|
'Save this password in the keyring? [y/N]: y\n'
|
|
'password is hunter2\n'
|
|
)
|
|
|
|
|
|
def test_get_password_from_cache(monkeypatch):
|
|
user = 'my_user'
|
|
resource = 'http://example.com'
|
|
|
|
@click.command()
|
|
@pass_context
|
|
def fake_app(ctx):
|
|
x = utils.password.get_password(user, resource)
|
|
click.echo('Password is {}'.format(x))
|
|
monkeypatch.setattr(click, 'prompt', blow_up)
|
|
|
|
assert (user, 'example.com') in ctx.passwords
|
|
x = utils.password.get_password(user, resource)
|
|
click.echo('Password is {}'.format(x))
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(fake_app, input='my_password\n')
|
|
assert not result.exception
|
|
assert result.output.splitlines() == [
|
|
'Server password for {} at host {}: '.format(user, 'example.com'),
|
|
'Save this password in the keyring? [y/N]: ',
|
|
'Password is my_password',
|
|
'Password is my_password'
|
|
]
|
|
|
|
|
|
def test_get_class_init_args():
|
|
class Foobar(object):
|
|
def __init__(self, foo, bar, baz=None):
|
|
pass
|
|
|
|
all, required = utils.get_class_init_args(Foobar)
|
|
assert all == {'foo', 'bar', 'baz'}
|
|
assert required == {'foo', 'bar'}
|
|
|
|
|
|
def test_get_class_init_args_on_storage():
|
|
from vdirsyncer.storage.memory import MemoryStorage
|
|
|
|
all, required = utils.get_class_init_args(MemoryStorage)
|
|
assert all == set(['fileext', 'collection', 'read_only', 'instance_name'])
|
|
assert not required
|
|
|
|
|
|
def test_request_ssl(httpsserver):
|
|
httpsserver.serve_content('') # we need to serve something
|
|
|
|
with pytest.raises(requests.exceptions.SSLError) as excinfo:
|
|
utils.http.request('GET', httpsserver.url)
|
|
assert 'certificate verify failed' in str(excinfo.value)
|
|
|
|
utils.http.request('GET', httpsserver.url, verify=False)
|
|
|
|
|
|
def _fingerprints_broken():
|
|
from pkg_resources import parse_version as ver
|
|
tolerant_python = (
|
|
utils.compat.PY2 and platform.python_implementation() != 'PyPy'
|
|
)
|
|
broken_urllib3 = ver(requests.__version__) <= ver('2.5.1')
|
|
return broken_urllib3 and not tolerant_python
|
|
|
|
|
|
@pytest.mark.skipif(_fingerprints_broken(),
|
|
reason='https://github.com/shazow/urllib3/issues/529')
|
|
@pytest.mark.parametrize('fingerprint', [
|
|
'94:FD:7A:CB:50:75:A4:69:82:0A:F8:23:DF:07:FC:69:3E:CD:90:CA',
|
|
'19:90:F7:23:94:F2:EF:AB:2B:64:2D:57:3D:25:95:2D'
|
|
])
|
|
def test_request_ssl_fingerprints(httpsserver, fingerprint):
|
|
httpsserver.serve_content('') # we need to serve something
|
|
|
|
utils.http.request('GET', httpsserver.url, verify=False,
|
|
verify_fingerprint=fingerprint)
|
|
with pytest.raises(requests.exceptions.SSLError) as excinfo:
|
|
utils.http.request('GET', httpsserver.url,
|
|
verify_fingerprint=fingerprint)
|
|
|
|
with pytest.raises(requests.exceptions.SSLError) as excinfo:
|
|
utils.http.request('GET', httpsserver.url, verify=False,
|
|
verify_fingerprint=''.join(reversed(fingerprint)))
|
|
assert 'Fingerprints did not match' in str(excinfo.value)
|