Auth: Introduce login(login, password) method
This deprecates map_login_to_user, is_authenticated and is_authenticated2
This commit is contained in:
parent
e96fa64fa6
commit
e73270bbe5
@ -431,6 +431,7 @@ class Application:
|
|||||||
authorization = environ.get("HTTP_AUTHORIZATION", "")
|
authorization = environ.get("HTTP_AUTHORIZATION", "")
|
||||||
if external_login:
|
if external_login:
|
||||||
login, password = external_login
|
login, password = external_login
|
||||||
|
login, password = login or "", password or ""
|
||||||
elif authorization.startswith("Basic"):
|
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(
|
||||||
@ -439,30 +440,28 @@ class Application:
|
|||||||
# DEPRECATED: use remote_user backend instead
|
# DEPRECATED: use remote_user backend instead
|
||||||
login = environ.get("REMOTE_USER", "")
|
login = environ.get("REMOTE_USER", "")
|
||||||
password = ""
|
password = ""
|
||||||
user = self.Auth.map_login_to_user(login)
|
|
||||||
|
|
||||||
if not user:
|
user = self.Auth.login(login, password) or "" if login else ""
|
||||||
is_authenticated = True
|
if user and login == user:
|
||||||
elif not storage.is_safe_path_component(user):
|
self.logger.info("Successful login: %r", user)
|
||||||
|
elif user:
|
||||||
|
self.logger.info("Successful login: %r -> %r", login, user)
|
||||||
|
elif login:
|
||||||
|
self.logger.info("Failed login attempt: %r", login)
|
||||||
|
# Random delay to avoid timing oracles and bruteforce attacks
|
||||||
|
delay = self.configuration.getfloat("auth", "delay")
|
||||||
|
if delay > 0:
|
||||||
|
random_delay = delay * (0.5 + random.random())
|
||||||
|
self.logger.debug("Sleeping %.3f seconds", random_delay)
|
||||||
|
time.sleep(random_delay)
|
||||||
|
|
||||||
|
if user and not storage.is_safe_path_component(user):
|
||||||
# Prevent usernames like "user/calendar.ics"
|
# Prevent usernames like "user/calendar.ics"
|
||||||
self.logger.info("Refused unsafe username: %r", user)
|
self.logger.info("Refused unsafe username: %r", user)
|
||||||
is_authenticated = False
|
user = ""
|
||||||
else:
|
|
||||||
is_authenticated = self.Auth.is_authenticated2(login, user,
|
|
||||||
password)
|
|
||||||
if not is_authenticated:
|
|
||||||
self.logger.info("Failed login attempt: %r", user)
|
|
||||||
# Random delay to avoid timing oracles and bruteforce attacks
|
|
||||||
delay = self.configuration.getfloat("auth", "delay")
|
|
||||||
if delay > 0:
|
|
||||||
random_delay = delay * (0.5 + random.random())
|
|
||||||
self.logger.debug("Sleeping %.3f seconds", random_delay)
|
|
||||||
time.sleep(random_delay)
|
|
||||||
else:
|
|
||||||
self.logger.info("Successful login: %r", user)
|
|
||||||
|
|
||||||
# Create principal collection
|
# Create principal collection
|
||||||
if user and is_authenticated:
|
if user:
|
||||||
principal_path = "/%s/" % user
|
principal_path = "/%s/" % user
|
||||||
if self.Rights.authorized(user, principal_path, "w"):
|
if self.Rights.authorized(user, principal_path, "w"):
|
||||||
with self.Collection.acquire_lock("r", user):
|
with self.Collection.acquire_lock("r", user):
|
||||||
@ -476,7 +475,7 @@ class Application:
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
self.logger.warning("Failed to create principal "
|
self.logger.warning("Failed to create principal "
|
||||||
"collection %r: %s", user, e)
|
"collection %r: %s", user, e)
|
||||||
is_authenticated = False
|
user = ""
|
||||||
else:
|
else:
|
||||||
self.logger.warning("Access to principal path %r denied by "
|
self.logger.warning("Access to principal path %r denied by "
|
||||||
"rights backend", principal_path)
|
"rights backend", principal_path)
|
||||||
@ -491,7 +490,7 @@ class Application:
|
|||||||
"Request body too large: %d", content_length)
|
"Request body too large: %d", content_length)
|
||||||
return response(*REQUEST_ENTITY_TOO_LARGE)
|
return response(*REQUEST_ENTITY_TOO_LARGE)
|
||||||
|
|
||||||
if is_authenticated:
|
if not login or user:
|
||||||
status, headers, answer = function(
|
status, headers, answer = function(
|
||||||
environ, base_prefix, path, user)
|
environ, base_prefix, path, user)
|
||||||
if (status, headers, answer) == NOT_ALLOWED:
|
if (status, headers, answer) == NOT_ALLOWED:
|
||||||
@ -500,8 +499,8 @@ class Application:
|
|||||||
else:
|
else:
|
||||||
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
|
||||||
user and is_authenticated) and not external_login:
|
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
|
||||||
|
@ -102,14 +102,26 @@ class BaseAuth:
|
|||||||
"""
|
"""
|
||||||
return ()
|
return ()
|
||||||
|
|
||||||
def is_authenticated2(self, login, user, password):
|
def login(self, login, password):
|
||||||
"""Validate credentials.
|
"""Check credentials and map login to internal user
|
||||||
|
|
||||||
``login`` the login name
|
``login`` the login name
|
||||||
|
|
||||||
``user`` the user from ``map_login_to_user(login)``.
|
``password`` the password
|
||||||
|
|
||||||
``password`` the login password
|
Returns the user name or ``""`` for invalid credentials.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
user = self.map_login_to_user(login)
|
||||||
|
if user and self.is_authenticated2(login, user, password):
|
||||||
|
return user
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def is_authenticated2(self, login, user, password):
|
||||||
|
"""Validate credentials.
|
||||||
|
|
||||||
|
DEPRECATED: use ``login`` instead
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return self.is_authenticated(user, password)
|
return self.is_authenticated(user, password)
|
||||||
@ -117,7 +129,7 @@ class BaseAuth:
|
|||||||
def is_authenticated(self, user, password):
|
def is_authenticated(self, user, password):
|
||||||
"""Validate credentials.
|
"""Validate credentials.
|
||||||
|
|
||||||
DEPRECATED: use ``is_authenticated2`` instead
|
DEPRECATED: use ``login`` instead
|
||||||
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@ -125,11 +137,7 @@ class BaseAuth:
|
|||||||
def map_login_to_user(self, login):
|
def map_login_to_user(self, login):
|
||||||
"""Map login name to internal user.
|
"""Map login name to internal user.
|
||||||
|
|
||||||
``login`` the login name, ``""`` for anonymous users
|
DEPRECATED: use ``login`` instead
|
||||||
|
|
||||||
Returns a string with the user name.
|
|
||||||
If a login can't be mapped to an user, return ``login`` and
|
|
||||||
return ``False`` in ``is_authenticated2(...)``.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
return login
|
return login
|
||||||
|
Loading…
Reference in New Issue
Block a user