FIX #55: Allow unauthenticated uploads with `-P .` option.

+ Add `mock` lib into test-dependencies for py2.
+ travis: Use new containers.
This commit is contained in:
Kostis Anagnostopoulos at STUW025 2015-09-11 15:10:41 +02:00
parent 47b80b414a
commit 1fd5653317
6 changed files with 59 additions and 13 deletions

View File

@ -1,3 +1,4 @@
sudo: false
language: python
python: 2.7
env:
@ -17,3 +18,4 @@ script:
branches:
except:
- standalone

View File

@ -216,14 +216,21 @@ Running ``pypi-server -h`` will print a detailed usage message::
-a, --authenticate (UPDATE|download|list), ...
comma-separated list of (case-insensitive) actions to authenticate
(requires giving also the -P option). For example to password-protect
package uploads & downloads while leaving listings public, give:
-a update,download.
Requires -P option and cannot not be empty unless -P is '.'
For example to password-protect package downloads (in addition to uploads)
while leaving listings public, give:
-P foo/htpasswd.txt -a update,download
To drop all authentications, use:
-P . -a ''
For example to password-protect package uploads & downloads while leaving
listings public, give:
-P -a update,download
By default, only 'update' is password-protected.
-P, --passwords PASSWORD_FILE
use apache htpasswd file PASSWORD_FILE to set usernames & passwords
used for authentication of certain actions (see -a option).
Set it explicitly to '.' to allow empty list of actions to authenticate.
--disable-fallback
disable redirect to real PyPI index for packages not found in the

View File

@ -209,14 +209,21 @@ pypi-server understands the following options:
-a, --authenticate (UPDATE|download|list), ...
comma-separated list of (case-insensitive) actions to authenticate
(requires giving also the -P option). For example to password-protect
package uploads & downloads while leaving listings public, give:
-a update,download.
Requires -P option and cannot not be empty unless -P is '.'
For example to password-protect package downloads (in addition to uploads)
while leaving listings public, give:
-P foo/htpasswd.txt -a update,download
To drop all authentications, use:
-P . -a ''
For example to password-protect package uploads & downloads while leaving
listings public, give:
-P -a update,download
By default, only 'update' is password-protected.
-P, --passwords PASSWORD_FILE
use apache htpasswd file PASSWORD_FILE to set usernames & passwords
used for authentication of certain actions (see -a option).
Set it explicitly to '.' to allow empty list of actions to authenticate.
--disable-fallback
disable redirect to real PyPI index for packages not found in the
@ -249,12 +256,12 @@ pypi-server understands the following options:
--log-frmt <FILE>
the logging format-string. (see `logging.LogRecord` class from standard python library)
[Default: %(asctime)s|%(levelname)s|%(thread)d|%(message)s]
[Default: %(asctime)s|%(levelname)s|%(thread)d|%(message)s]
--log-req-frmt FORMAT
a format-string selecting Http-Request properties to log; set to '%s' to see them all.
[Default: %(bottle.request)s]
[Default: %(bottle.request)s]
--log-res-frmt FORMAT
a format-string selecting Http-Response properties to log; set to '%s' to see them all.
[Default: %(status)s]
@ -354,7 +361,9 @@ def main(argv=None):
if k in ("-p", "--port"):
port = int(v)
elif k in ("-a", "--authenticate"):
authenticated = [a.lower() for a in re.split("[, ]+", v.strip(" ,"))]
authenticated = [a.lower()
for a in re.split("[, ]+", v.strip(" ,"))
if a]
actions = ("list", "download", "update")
for a in authenticated:
if a not in actions:
@ -408,8 +417,8 @@ def main(argv=None):
usage()
sys.exit(0)
if password_file and not (password_file and authenticated):
sys.exit("Must give both password file (-P) and actions to authenticate (-a).")
if password_file and password_file != '.' and not authenticated:
sys.exit("Actions to authenticate (-a) must not be empty, unless password file (-P) is '.'!")
if len(roots) == 0:
roots.append(os.path.expanduser("~/packages"))

5
setup.py Executable file → Normal file
View File

@ -14,8 +14,10 @@ except ImportError:
if sys.version_info >= (3, 0):
exec("def do_exec(co, loc): exec(co, loc)\n")
tests_require = []
else:
exec("def do_exec(co, loc): exec co in loc\n")
tests_require = ['mock']
def get_version():
@ -33,7 +35,8 @@ setup(name="pypiserver",
version=get_version(),
packages=["pypiserver"],
package_data={'pypiserver': ['welcome.html']},
url="https://github.com/pypiserver/pypiserver",
tests_require=tests_require,
url="https://github.com/pypiserver/pypiserver",
maintainer="Ralf Schmitt, Kostis Anagnostopoulos",
maintainer_email="ralf@systemexit.de, ankostis@gmail.com",
classifiers=[

23
tests/test_main.py Executable file → Normal file
View File

@ -2,6 +2,10 @@
import sys, os, pytest, logging
from pypiserver import core
try:
from unittest import mock
except ImportError:
import mock
class main_wrapper(object):
@ -111,3 +115,22 @@ def test_welcome_file(main):
def test_welcome_file_default(main):
main([])
assert "Welcome to pypiserver!" in main.app.module.config.welcome_msg
def test_password_without_auth_list(main, monkeypatch):
sysexit = mock.MagicMock(side_effect=ValueError('BINGO'))
monkeypatch.setattr('sys.exit', sysexit)
with pytest.raises(ValueError) as excinfo:
main(["-P", "pswd-file", "-a", ""])
assert excinfo.value.args[0] == 'BINGO'
def test_password_alone(main, monkeypatch):
monkeypatch.setitem(sys.modules, 'passlib', mock.MagicMock())
monkeypatch.setitem(sys.modules, 'passlib.apache', mock.MagicMock())
main(["-P", "pswd-file"])
assert main.app.module.config.authenticated == ['update']
def test_dot_password_without_auth_list(main, monkeypatch):
monkeypatch.setitem(sys.modules, 'passlib', mock.MagicMock())
monkeypatch.setitem(sys.modules, 'passlib.apache', mock.MagicMock())
main(["-P", ".", "-a", ""])
assert main.app.module.config.authenticated == []

View File

@ -5,6 +5,7 @@ envlist = py25,py26,py27,py32,py33,py34
deps=pytest>=2.3
webtest
beautifulsoup4
mock
commands=py.test []
sitepackages=False
@ -13,6 +14,7 @@ deps=pytest>=2.3
WebTest==1.4.3
WebOb==0.9.6.1
BeautifulSoup==3.2.1
mock
[pytest]
norecursedirs = bin parts develop-eggs eggs .* _* CVS {args}