forked from github.com/pypiserver
Removed passlib to plugin, no-auth defaults, integration tests
This commit is contained in:
parent
84158ab881
commit
824c2dd24f
@ -148,6 +148,10 @@ class _PypiserverParser(ArgumentParser):
|
||||
parsed = super(_PypiserverParser, self).parse_args(
|
||||
args=args, namespace=namespace
|
||||
)
|
||||
if not hasattr(parsed, 'authenticate'):
|
||||
# Ensure a useful value is present even when no auth
|
||||
# plugins are installed.
|
||||
parsed.authenticate = _Defaults.authenticate
|
||||
for attr, parser in self.extra_parsers.items():
|
||||
if hasattr(parsed, attr):
|
||||
setattr(parsed, attr, parser(getattr(parsed, attr)))
|
||||
@ -410,31 +414,42 @@ class Config(object):
|
||||
|
||||
:param ArgumentParser parser: an ArgumentParser instance
|
||||
"""
|
||||
auth_plugins_available = bool(
|
||||
set(self._plugins['authenticators']).difference(
|
||||
set(('no-auth',))
|
||||
)
|
||||
)
|
||||
security = parser.add_argument_group(
|
||||
title='Security',
|
||||
description='Configure pypiserver access controls'
|
||||
)
|
||||
# TODO: pull some of this long stuff out into an epilog
|
||||
security.add_argument(
|
||||
'-a', '--authenticate',
|
||||
default=environ.get(
|
||||
'PYPISERVER_AUTHENTICATE',
|
||||
_Defaults.authenticate,
|
||||
),
|
||||
help=dedent('''\
|
||||
comma-separated list of (case-insensitive) actions to
|
||||
authenticate. Use "." for no authentication. Requires the
|
||||
password (-P option) to be set. For example to password-protect
|
||||
package downloads (in addition to uploads), while leaving
|
||||
listings public, use: `-P foo/htpasswd.txt` -a update,download
|
||||
To drop all authentications, use: `-P . -a `.
|
||||
Note that when uploads are not protected, the `register`
|
||||
command is not necessary, but `~/.pypirc` still requires
|
||||
username and password fields, even if bogus. By default,
|
||||
only %(default)s is password-protected
|
||||
''')
|
||||
)
|
||||
if auth_plugins_available or self.parser_type == 'pypi-server':
|
||||
# Do not bother to show authentication arguments when no
|
||||
# non-dummy auth plugins are installed.
|
||||
security.add_argument(
|
||||
'-a', '--authenticate',
|
||||
default=environ.get(
|
||||
'PYPISERVER_AUTHENTICATE',
|
||||
_Defaults.authenticate,
|
||||
),
|
||||
# TODO: pull some of this long stuff out into an epilog
|
||||
help=dedent('''\
|
||||
comma-separated list of (case-insensitive) actions to
|
||||
authenticate. Use "." for no authentication. Requires the
|
||||
password (-P option) to be set. For example to
|
||||
password-protect package downloads (in addition to
|
||||
uploads), while leaving listings public, use:
|
||||
`-P foo/htpasswd.txt -a update,download`.
|
||||
To drop all authentications, use: `-P . -a .`.
|
||||
Note that when uploads are not protected, the `register`
|
||||
command is not necessary, but `~/.pypirc` still requires
|
||||
username and password fields, even if bogus. By default,
|
||||
only %(default)s is password-protected
|
||||
''')
|
||||
)
|
||||
if self.parser_type == 'pypi-server':
|
||||
# This argument is created by the `pypiserver-passlib` plugin
|
||||
# for pypiserver>=2.0
|
||||
security.add_argument(
|
||||
'-P', '--passwords',
|
||||
dest='password_file',
|
||||
|
@ -1,44 +0,0 @@
|
||||
"""Authentication based on an htpasswd file."""
|
||||
|
||||
from os import environ
|
||||
from textwrap import dedent
|
||||
|
||||
from passlib.apache import HtpasswdFile
|
||||
|
||||
from .interface import AuthenticatorInterface
|
||||
|
||||
|
||||
class HtpasswdAuthenticator(AuthenticatorInterface):
|
||||
"""Authenticate using passlib and an htpasswd file."""
|
||||
|
||||
plugin_name = 'Htpasswd Authenticator'
|
||||
plugin_help = 'Authenticate using an Apache htpasswd file'
|
||||
|
||||
def __init__(self, config):
|
||||
"""Instantiate the authenticator."""
|
||||
self.config = config
|
||||
|
||||
@classmethod
|
||||
def update_parser(cls, parser):
|
||||
"""Add htpasswd arguments to the config parser.
|
||||
|
||||
:param argparse.ArgumentParser parser: the config parser
|
||||
"""
|
||||
parser.add_argument(
|
||||
'-P', '--passwords',
|
||||
dest='password_file',
|
||||
default=environ.get('PYPISERVER_PASSWORD_FILE'),
|
||||
help=dedent('''\
|
||||
use apache htpasswd file PASSWORD_FILE to set usernames &
|
||||
passwords when authenticating certain actions (see -a option).
|
||||
''')
|
||||
)
|
||||
|
||||
def authenticate(self, request):
|
||||
"""Authenticate the provided request."""
|
||||
if (self.config.password_file is None or
|
||||
self.config.password_file == '.'):
|
||||
return True
|
||||
pwd_file = HtpasswdFile(self.config.password_file)
|
||||
pwd_file.load_if_changed()
|
||||
return pwd_file.check_password(*request.auth)
|
@ -19,4 +19,4 @@ tox
|
||||
twine>=1.7
|
||||
virtualenv
|
||||
webtest
|
||||
wheel>=0.25.0
|
||||
wheel>=0.25.0
|
||||
|
10
setup.py
10
setup.py
@ -42,12 +42,12 @@ setup(
|
||||
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
|
||||
setup_requires=setup_requires,
|
||||
extras_require={
|
||||
'passlib': ['passlib>=1.6'],
|
||||
'passlib': ['pypiserver-passlib'],
|
||||
'cache': ['watchdog']
|
||||
},
|
||||
tests_require=tests_require,
|
||||
url="https://github.com/pypiserver/pypiserver",
|
||||
maintainer=("Kostis Anagnostopoulos <ankostis@gmail.com>"
|
||||
maintainer=("Kostis Anagnostopoulos <ankostis@gmail.com> "
|
||||
"Matthew Planchard <mplanchard@gmail.com>"),
|
||||
maintainer_email="ankostis@gmail.com",
|
||||
classifiers=[
|
||||
@ -70,7 +70,8 @@ setup(
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Topic :: Software Development :: Build Tools",
|
||||
"Topic :: System :: Software Distribution"],
|
||||
"Topic :: System :: Software Distribution"
|
||||
],
|
||||
zip_safe=True,
|
||||
entry_points={
|
||||
'paste.app_factory': ['main=pypiserver.paste:paste_app_factory'],
|
||||
@ -79,9 +80,6 @@ setup(
|
||||
'pypiserver=pypiserver.__main__:main',
|
||||
],
|
||||
'pypiserver.authenticators': [
|
||||
'htpasswd = '
|
||||
'pypiserver.plugins.authenticators.htpasswd:HtpasswdAuthenticator '
|
||||
'[passlib]',
|
||||
'no-auth = '
|
||||
'pypiserver.plugins.authenticators.no_auth:NoAuthAuthenticator'
|
||||
]
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
from os import chdir, getcwd, environ, path, remove
|
||||
from os import chdir, getcwd, environ, listdir, path, remove
|
||||
from shutil import copy2, rmtree
|
||||
from subprocess import PIPE, Popen
|
||||
from tempfile import mkdtemp
|
||||
@ -52,35 +52,52 @@ def activate_venv(venv_dir):
|
||||
environ['VIRTUAL_ENV'] = start_venv
|
||||
|
||||
|
||||
def pypiserver_cmd(venv_dir, root, *args):
|
||||
def pypiserver_cmd(root, *args):
|
||||
"""Yield a command to run pypiserver.
|
||||
|
||||
:param str exc: the path to the python executable to use in
|
||||
running pypiserver.
|
||||
:param args: extra arguments for ``pypiserver run``
|
||||
"""
|
||||
yield '{}/bin/pypiserver'.format(venv_dir)
|
||||
# yield '{}/bin/pypiserver'.format(venv_dir)
|
||||
yield 'pypiserver'
|
||||
yield 'run'
|
||||
yield root
|
||||
for arg in args:
|
||||
yield arg
|
||||
|
||||
|
||||
def pip_cmd(venv_dir, *args):
|
||||
def pip_cmd(*args):
|
||||
"""Yield a command to run pip.
|
||||
|
||||
:param str bindir: the path to the bin directory where the pip
|
||||
command can be found.
|
||||
:param args: extra arguments for ``pip``
|
||||
"""
|
||||
yield '{}/bin/pip'.format(venv_dir)
|
||||
yield 'pip'
|
||||
for arg in args:
|
||||
yield arg
|
||||
if 'install' in args or 'download' in args:
|
||||
yield '--index-url'
|
||||
if any(i in args for i in ('install', 'download', 'search')):
|
||||
yield '-i'
|
||||
yield 'http://localhost:8080'
|
||||
|
||||
|
||||
def twine_cmd(*args):
|
||||
"""Yield a command to run twine.
|
||||
|
||||
:param args: arguments for `twine`
|
||||
"""
|
||||
yield 'twine'
|
||||
for arg in args:
|
||||
yield arg
|
||||
for part in ('--repository-url', 'http://localhost:8080'):
|
||||
yield part
|
||||
if '-u' not in args:
|
||||
for part in ('-u', 'username'):
|
||||
yield part
|
||||
if '-p' not in args:
|
||||
for part in ('-p', 'password'):
|
||||
yield part
|
||||
|
||||
|
||||
def run(args, raise_on_err=True, capture=False, **kwargs):
|
||||
"""Straightforward implementation to run subprocesses.
|
||||
|
||||
@ -112,7 +129,6 @@ def venv():
|
||||
venv_dir,
|
||||
))
|
||||
with activate_venv(venv_dir):
|
||||
import ipdb; ipdb.set_trace()
|
||||
run(
|
||||
(
|
||||
'python',
|
||||
@ -161,9 +177,7 @@ class TestNoAuth:
|
||||
"""Run pypiserver with no auth."""
|
||||
pkg_root = mkdtemp()
|
||||
with activate_venv(venv):
|
||||
proc = Popen(pypiserver_cmd(
|
||||
venv, pkg_root, '--auth-backend', 'no-auth'
|
||||
), env=environ)
|
||||
proc = Popen(pypiserver_cmd(pkg_root), env=environ)
|
||||
yield pkg_root
|
||||
proc.kill()
|
||||
rmtree(pkg_root)
|
||||
@ -182,10 +196,21 @@ class TestNoAuth:
|
||||
yield
|
||||
remove(path.join(pkg_root, SIMPLE_DEV_PKG))
|
||||
|
||||
@pytest.mark.usefixtures('venv_active')
|
||||
def test_install(self, venv, simple_pkg):
|
||||
@pytest.mark.usefixtures('venv_active', 'simple_pkg')
|
||||
def test_install(self):
|
||||
"""Test pulling a package with pip from the repo."""
|
||||
run(('pip', 'install', 'simple_pkg'))
|
||||
assert 'simple-pkg' in run(
|
||||
('pip', 'freeze'), capture=True, env=environ
|
||||
)
|
||||
run(pip_cmd('install', 'simple_pkg'))
|
||||
assert 'simple-pkg' in run(pip_cmd('freeze'), capture=True)
|
||||
|
||||
@pytest.mark.usefixtures('venv_active')
|
||||
def test_upload(self, pkg_root):
|
||||
"""Test putting a package into the rpeo."""
|
||||
assert SIMPLE_PKG not in listdir(pkg_root)
|
||||
run(twine_cmd('upload', SIMPLE_PKG_PATH))
|
||||
assert SIMPLE_PKG in listdir(pkg_root)
|
||||
|
||||
@pytest.mark.usefixtures('venv_active', 'simple_pkg')
|
||||
def test_search(self):
|
||||
"""Test results of pip search."""
|
||||
out = run(pip_cmd('search', 'simple_pkg'), capture=True)
|
||||
assert 'simple_pkg' in out
|
||||
|
Loading…
Reference in New Issue
Block a user