Merge pull request #612 from Unrud/auth

Allow auth backends to provide login and password
This commit is contained in:
Unrud 2017-05-31 02:16:41 +02:00 committed by GitHub
commit 824835bcd4
4 changed files with 60 additions and 6 deletions

2
config
View File

@ -66,7 +66,7 @@
[auth]
# Authentication method
# Value: None | htpasswd
# Value: None | htpasswd | remote_user | http_x_remote_user
#type = None
# Htpasswd filename

View File

@ -371,15 +371,19 @@ class Application:
function = getattr(self, "do_%s" % environ["REQUEST_METHOD"].upper())
# Ask authentication backend to check rights
authorization = environ.get("HTTP_AUTHORIZATION", None)
if authorization and authorization.startswith("Basic"):
external_login = self.Auth.get_external_login(environ)
authorization = environ.get("HTTP_AUTHORIZATION", "")
if external_login:
login, password = external_login
elif authorization.startswith("Basic"):
authorization = authorization[len("Basic"):].strip()
login, password = self.decode(base64.b64decode(
authorization.encode("ascii")), environ).split(":", 1)
user = self.Auth.map_login_to_user(login)
else:
user = self.Auth.map_login_to_user(environ.get("REMOTE_USER", ""))
# DEPRECATED: use remote_user backend instead
login = environ.get("REMOTE_USER", "")
password = ""
user = self.Auth.map_login_to_user(login)
# If "/.well-known" is not available, clients query "/"
if path == "/.well-known" or path.startswith("/.well-known/"):
@ -437,7 +441,7 @@ class Application:
status, headers, answer = NOT_ALLOWED
if (status, headers, answer) == NOT_ALLOWED and not (
user and is_authenticated):
user and is_authenticated) and not external_login:
# Unknown or unauthorized user
self.logger.debug("Asking client for authentication")
status = client.UNAUTHORIZED

View File

@ -67,6 +67,10 @@ def load(configuration, logger):
logger.debug("Authentication type is %s", auth_type)
if auth_type == "None":
class_ = NoneAuth
elif auth_type == "remote_user":
class_ = RemoteUserAuth
elif auth_type == "http_x_remote_user":
class_ = HttpXRemoteUserAuth
elif auth_type == "htpasswd":
class_ = Auth
else:
@ -79,6 +83,14 @@ class BaseAuth:
self.configuration = configuration
self.logger = logger
def get_external_login(self, environ):
"""Optionally provide the login and password externally.
Returns a tuple (login, password) or ().
"""
return ()
def is_authenticated(self, user, password):
"""Validate credentials.
@ -201,3 +213,13 @@ class Auth(BaseAuth):
if login_ok & password_ok:
return True
return False
class RemoteUserAuth(NoneAuth):
def get_external_login(self, environ):
return environ.get("REMOTE_USER", ""), ""
class HttpXRemoteUserAuth(NoneAuth):
def get_external_login(self, environ):
return environ.get("HTTP_X_REMOTE_USER", ""), ""

View File

@ -109,6 +109,34 @@ class TestBaseAuthRequests(BaseTest):
"bcrypt",
"tmp:$2y$05$oD7hbiQFQlvCM7zoalo/T.MssV3VNTRI3w5KDnj8NTUKJNWfVpvRq")
def test_remote_user(self):
self.configuration.set("auth", "type", "remote_user")
self.application = Application(self.configuration, self.logger)
status, _, answer = self.request(
"PROPFIND", "/",
"""<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", REMOTE_USER="test")
assert status == 207
assert ">/test/<" in answer
def test_http_x_remote_user(self):
self.configuration.set("auth", "type", "http_x_remote_user")
self.application = Application(self.configuration, self.logger)
status, _, answer = self.request(
"PROPFIND", "/",
"""<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", HTTP_X_REMOTE_USER="test")
assert status == 207
assert ">/test/<" in answer
def test_custom(self):
"""Custom authentication."""
self.configuration.set("auth", "type", "tests.custom.auth")