mirror of
https://github.com/pypiserver/pypiserver
synced 2024-11-09 16:45:51 +01:00
Merge pull request #66 from koodaamo/auth_improvements
Merge Auth improvements (directly on github).
This commit is contained in:
commit
202c26fb75
18
README.rst
18
README.rst
@ -88,9 +88,16 @@ pypi-server -h will print a detailed usage message::
|
||||
-i INTERFACE, --interface INTERFACE
|
||||
listen on interface INTERFACE (default: 0.0.0.0, any interface)
|
||||
|
||||
-a (update|download|list), ... --authenticate (update|download|list), ...
|
||||
comma-separated list of actions to authenticate (requires giving also
|
||||
the -P option). For example to password-protect package uploads and
|
||||
downloads while leaving listings public, give: -a update,download.
|
||||
Note: make sure there is no space around the comma(s); otherwise, an
|
||||
error will occur.
|
||||
|
||||
-P PASSWORD_FILE, --passwords PASSWORD_FILE
|
||||
use apache htpasswd file PASSWORD_FILE in order to enable password
|
||||
protected uploads.
|
||||
use apache htpasswd file PASSWORD_FILE to set usernames & passwords
|
||||
used for authentication (requires giving the -s option as well).
|
||||
|
||||
--disable-fallback
|
||||
disable redirect to real PyPI index for packages not found in the
|
||||
@ -468,6 +475,13 @@ proxypypi (https://pypi.python.org/pypi/proxypypi)
|
||||
|
||||
Changelog
|
||||
=========
|
||||
|
||||
1.1.7 (2015-01-10)
|
||||
------------------
|
||||
- support password protected package listings and downloads,
|
||||
in addition to uploads (use the -a, --authenticate option
|
||||
to specify which to protect).
|
||||
|
||||
1.1.6 (2014-03-05)
|
||||
------------------
|
||||
- remove --index-url cli parameter introduced in 1.1.5
|
||||
|
@ -5,6 +5,7 @@ version = __version__ = "1.1.6"
|
||||
def app(root=None,
|
||||
redirect_to_fallback=True,
|
||||
fallback_url=None,
|
||||
authenticated=[],
|
||||
password_file=None,
|
||||
overwrite=False,
|
||||
log_req_frmt="%(bottle.request)s",
|
||||
@ -25,7 +26,7 @@ def app(root=None,
|
||||
fallback_url = "http://pypi.python.org/simple"
|
||||
|
||||
_app.configure(root=root, redirect_to_fallback=redirect_to_fallback, fallback_url=fallback_url,
|
||||
password_file=password_file, overwrite=overwrite,
|
||||
authenticated=authenticated, password_file=password_file, overwrite=overwrite,
|
||||
log_req_frmt=log_req_frmt, log_res_frmt=log_res_frmt, log_err_frmt=log_err_frmt)
|
||||
_app.app.module = _app
|
||||
|
||||
|
@ -33,9 +33,29 @@ def validate_user(username, password):
|
||||
return config.htpasswdfile.check_password(username, password)
|
||||
|
||||
|
||||
class auth(object):
|
||||
"decorator to apply authentication if specified for the decorated method & action"
|
||||
|
||||
def __init__(self, action):
|
||||
self.action = action
|
||||
|
||||
def __call__(self, method):
|
||||
|
||||
def protector(*args, **kwargs):
|
||||
if self.action in config.authenticated:
|
||||
if not request.auth or request.auth[1] is None:
|
||||
raise HTTPError(401, header={"WWW-Authenticate": 'Basic realm="pypi"'})
|
||||
if not validate_user(*request.auth):
|
||||
raise HTTPError(403)
|
||||
return method(*args, **kwargs)
|
||||
|
||||
return protector
|
||||
|
||||
|
||||
def configure(root=None,
|
||||
redirect_to_fallback=True,
|
||||
fallback_url=None,
|
||||
authenticated=[],
|
||||
password_file=None,
|
||||
overwrite=False,
|
||||
log_req_frmt=None,
|
||||
@ -46,12 +66,15 @@ def configure(root=None,
|
||||
log.info("Starting(%s)", dict(root=root,
|
||||
redirect_to_fallback=redirect_to_fallback,
|
||||
fallback_url=fallback_url,
|
||||
authenticated=authenticated,
|
||||
password_file=password_file,
|
||||
overwrite=overwrite,
|
||||
log_req_frmt=log_req_frmt,
|
||||
log_res_frmt=log_res_frmt,
|
||||
log_err_frmt=log_err_frmt))
|
||||
|
||||
config.authenticated = authenticated
|
||||
|
||||
if root is None:
|
||||
root = os.path.expanduser("~/packages")
|
||||
|
||||
@ -146,13 +169,8 @@ easy_install -i %(URL)ssimple/ PACKAGE
|
||||
|
||||
|
||||
@app.post('/')
|
||||
@auth("update")
|
||||
def update():
|
||||
if not request.auth or request.auth[1] is None:
|
||||
raise HTTPError(401, header={"WWW-Authenticate": 'Basic realm="pypi"'})
|
||||
|
||||
if not validate_user(*request.auth):
|
||||
raise HTTPError(403)
|
||||
|
||||
try:
|
||||
action = request.forms[':action']
|
||||
except KeyError:
|
||||
@ -208,11 +226,13 @@ def update():
|
||||
|
||||
|
||||
@app.route("/simple")
|
||||
@auth("list")
|
||||
def simpleindex_redirect():
|
||||
return redirect(request.fullpath + "/")
|
||||
|
||||
|
||||
@app.route("/simple/")
|
||||
@auth("list")
|
||||
def simpleindex():
|
||||
res = ["<html><head><title>Simple Index</title></head><body>\n"]
|
||||
for x in sorted(get_prefixes(packages())):
|
||||
@ -223,6 +243,7 @@ def simpleindex():
|
||||
|
||||
@app.route("/simple/:prefix")
|
||||
@app.route("/simple/:prefix/")
|
||||
@auth("list")
|
||||
def simple(prefix=""):
|
||||
fp = request.fullpath
|
||||
if not fp.endswith("/"):
|
||||
@ -246,6 +267,7 @@ def simple(prefix=""):
|
||||
|
||||
@app.route('/packages')
|
||||
@app.route('/packages/')
|
||||
@auth("list")
|
||||
def list_packages():
|
||||
fp = request.fullpath
|
||||
if not fp.endswith("/"):
|
||||
@ -262,6 +284,7 @@ def list_packages():
|
||||
|
||||
|
||||
@app.route('/packages/:filename#.*#')
|
||||
@auth("download")
|
||||
def server_static(filename):
|
||||
entries = find_packages(packages())
|
||||
for x in entries:
|
||||
|
@ -189,9 +189,16 @@ pypi-server understands the following options:
|
||||
-i INTERFACE, --interface INTERFACE
|
||||
listen on interface INTERFACE (default: 0.0.0.0, any interface)
|
||||
|
||||
-a (update|download|list), ... --authenticate (update|download|list), ...
|
||||
comma-separated list of actions to authenticate (requires giving also
|
||||
the -P option). For example to password-protect package uploads and
|
||||
downloads while leaving listings public, give: -a update,download.
|
||||
Note: make sure there is no space around the comma(s); otherwise, an
|
||||
error will occur.
|
||||
|
||||
-P PASSWORD_FILE, --passwords PASSWORD_FILE
|
||||
use apache htpasswd file PASSWORD_FILE in order to enable password
|
||||
protected uploads.
|
||||
use apache htpasswd file PASSWORD_FILE to set usernames & passwords
|
||||
used for authentication (requires giving the -s option as well).
|
||||
|
||||
--disable-fallback
|
||||
disable redirect to real PyPI index for packages not found in the
|
||||
@ -275,6 +282,7 @@ def main(argv=None):
|
||||
server = DEFAULT_SERVER
|
||||
redirect_to_fallback = True
|
||||
fallback_url = "http://pypi.python.org/simple"
|
||||
authenticated = []
|
||||
password_file = None
|
||||
overwrite = False
|
||||
verbosity = 1
|
||||
@ -289,9 +297,10 @@ def main(argv=None):
|
||||
update_stable_only = True
|
||||
|
||||
try:
|
||||
opts, roots = getopt.getopt(argv[1:], "i:p:r:d:P:Uuvxoh", [
|
||||
opts, roots = getopt.getopt(argv[1:], "i:p:a:r:d:P:Uuvxoh", [
|
||||
"interface=",
|
||||
"passwords=",
|
||||
"authenticate=",
|
||||
"port=",
|
||||
"root=",
|
||||
"server=",
|
||||
@ -313,6 +322,13 @@ def main(argv=None):
|
||||
for k, v in opts:
|
||||
if k in ("-p", "--port"):
|
||||
port = int(v)
|
||||
elif k in ("-a", "--authenticate"):
|
||||
authenticated = [a.strip() for a in v.strip(',').split(',')]
|
||||
actions = ("list", "download", "update")
|
||||
for a in authenticated:
|
||||
if a not in actions:
|
||||
errmsg = "Incorrect action '%s' given with option '%s'" % (a, k)
|
||||
sys.exit(errmsg)
|
||||
elif k in ("-i", "--interface"):
|
||||
host = v
|
||||
elif k in ("-r", "--root"):
|
||||
@ -357,6 +373,9 @@ def main(argv=None):
|
||||
usage()
|
||||
sys.exit(0)
|
||||
|
||||
if (password_file or authenticated) and not (password_file and authenticated):
|
||||
sys.exit("Must give both password file (-P) and actions to authenticate (-a).")
|
||||
|
||||
if len(roots) == 0:
|
||||
roots.append(os.path.expanduser("~/packages"))
|
||||
|
||||
@ -375,6 +394,7 @@ def main(argv=None):
|
||||
a = app(
|
||||
root=roots,
|
||||
redirect_to_fallback=redirect_to_fallback,
|
||||
authenticated=authenticated,
|
||||
password_file=password_file,
|
||||
fallback_url=fallback_url,
|
||||
overwrite=overwrite,
|
||||
|
Loading…
Reference in New Issue
Block a user