Merge pull request #612 from Unrud/auth
Allow auth backends to provide login and password
This commit is contained in:
commit
824835bcd4
2
config
2
config
@ -66,7 +66,7 @@
|
|||||||
[auth]
|
[auth]
|
||||||
|
|
||||||
# Authentication method
|
# Authentication method
|
||||||
# Value: None | htpasswd
|
# Value: None | htpasswd | remote_user | http_x_remote_user
|
||||||
#type = None
|
#type = None
|
||||||
|
|
||||||
# Htpasswd filename
|
# Htpasswd filename
|
||||||
|
@ -371,15 +371,19 @@ class Application:
|
|||||||
function = getattr(self, "do_%s" % environ["REQUEST_METHOD"].upper())
|
function = getattr(self, "do_%s" % environ["REQUEST_METHOD"].upper())
|
||||||
|
|
||||||
# Ask authentication backend to check rights
|
# Ask authentication backend to check rights
|
||||||
authorization = environ.get("HTTP_AUTHORIZATION", None)
|
external_login = self.Auth.get_external_login(environ)
|
||||||
if authorization and authorization.startswith("Basic"):
|
authorization = environ.get("HTTP_AUTHORIZATION", "")
|
||||||
|
if external_login:
|
||||||
|
login, password = external_login
|
||||||
|
elif authorization.startswith("Basic"):
|
||||||
authorization = authorization[len("Basic"):].strip()
|
authorization = authorization[len("Basic"):].strip()
|
||||||
login, password = self.decode(base64.b64decode(
|
login, password = self.decode(base64.b64decode(
|
||||||
authorization.encode("ascii")), environ).split(":", 1)
|
authorization.encode("ascii")), environ).split(":", 1)
|
||||||
user = self.Auth.map_login_to_user(login)
|
|
||||||
else:
|
else:
|
||||||
user = self.Auth.map_login_to_user(environ.get("REMOTE_USER", ""))
|
# DEPRECATED: use remote_user backend instead
|
||||||
|
login = environ.get("REMOTE_USER", "")
|
||||||
password = ""
|
password = ""
|
||||||
|
user = self.Auth.map_login_to_user(login)
|
||||||
|
|
||||||
# If "/.well-known" is not available, clients query "/"
|
# If "/.well-known" is not available, clients query "/"
|
||||||
if path == "/.well-known" or path.startswith("/.well-known/"):
|
if path == "/.well-known" or path.startswith("/.well-known/"):
|
||||||
@ -437,7 +441,7 @@ class Application:
|
|||||||
status, headers, answer = NOT_ALLOWED
|
status, headers, answer = NOT_ALLOWED
|
||||||
|
|
||||||
if (status, headers, answer) == NOT_ALLOWED and not (
|
if (status, headers, answer) == NOT_ALLOWED and not (
|
||||||
user and is_authenticated):
|
user and is_authenticated) and not external_login:
|
||||||
# Unknown or unauthorized user
|
# Unknown or unauthorized user
|
||||||
self.logger.debug("Asking client for authentication")
|
self.logger.debug("Asking client for authentication")
|
||||||
status = client.UNAUTHORIZED
|
status = client.UNAUTHORIZED
|
||||||
|
@ -67,6 +67,10 @@ def load(configuration, logger):
|
|||||||
logger.debug("Authentication type is %s", auth_type)
|
logger.debug("Authentication type is %s", auth_type)
|
||||||
if auth_type == "None":
|
if auth_type == "None":
|
||||||
class_ = NoneAuth
|
class_ = NoneAuth
|
||||||
|
elif auth_type == "remote_user":
|
||||||
|
class_ = RemoteUserAuth
|
||||||
|
elif auth_type == "http_x_remote_user":
|
||||||
|
class_ = HttpXRemoteUserAuth
|
||||||
elif auth_type == "htpasswd":
|
elif auth_type == "htpasswd":
|
||||||
class_ = Auth
|
class_ = Auth
|
||||||
else:
|
else:
|
||||||
@ -79,6 +83,14 @@ class BaseAuth:
|
|||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
self.logger = logger
|
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):
|
def is_authenticated(self, user, password):
|
||||||
"""Validate credentials.
|
"""Validate credentials.
|
||||||
|
|
||||||
@ -201,3 +213,13 @@ class Auth(BaseAuth):
|
|||||||
if login_ok & password_ok:
|
if login_ok & password_ok:
|
||||||
return True
|
return True
|
||||||
return False
|
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", ""), ""
|
||||||
|
@ -109,6 +109,34 @@ class TestBaseAuthRequests(BaseTest):
|
|||||||
"bcrypt",
|
"bcrypt",
|
||||||
"tmp:$2y$05$oD7hbiQFQlvCM7zoalo/T.MssV3VNTRI3w5KDnj8NTUKJNWfVpvRq")
|
"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):
|
def test_custom(self):
|
||||||
"""Custom authentication."""
|
"""Custom authentication."""
|
||||||
self.configuration.set("auth", "type", "tests.custom.auth")
|
self.configuration.set("auth", "type", "tests.custom.auth")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user