diff --git a/kallithea/lib/auth.py b/kallithea/lib/auth.py --- a/kallithea/lib/auth.py +++ b/kallithea/lib/auth.py @@ -473,11 +473,12 @@ class AuthUser(object): "default". Use `is_anonymous` to check for both "default" and "no user". """ - def __init__(self, user_id=None, dbuser=None, + def __init__(self, user_id=None, dbuser=None, authenticating_api_key=None, is_external_auth=False): self.is_authenticated = False self.is_external_auth = is_external_auth + self.authenticating_api_key = authenticating_api_key user_model = UserModel() self._default_user = User.get_default_user(cache=True) @@ -738,23 +739,19 @@ class LoginRequired(object): if not AuthUser.check_ip_allowed(user, controller.ip_addr): raise _redirect_to_login(_('IP %s not allowed') % controller.ip_addr) - # check if we used an API key and it's a valid one - api_key = request.GET.get('api_key') + # Check if we used an API key to authenticate. + api_key = user.authenticating_api_key if api_key is not None: - # explicit controller is enabled or API is in our whitelist - if self.api_access or allowed_api_access(loc, api_key=api_key): - if api_key in user.api_keys: - log.info('user %s authenticated with API key ****%s @ %s', - user, api_key[-4:], loc) - return func(*fargs, **fkwargs) - else: - log.warning('API key ****%s is NOT valid', api_key[-4:]) - raise _redirect_to_login(_('Invalid API key')) - else: + # Check that controller is enabled for API key usage. + if not self.api_access and not allowed_api_access(loc, api_key=api_key): # controller does not allow API access log.warning('API access to %s is not allowed', loc) raise HTTPForbidden() + log.info('user %s authenticated with API key ****%s @ %s', + user, api_key[-4:], loc) + return func(*fargs, **fkwargs) + # CSRF protection: Whenever a request has ambient authority (whether # through a session cookie or its origin IP address), it must include # the correct token, unless the HTTP method is GET or HEAD (and thus diff --git a/kallithea/lib/base.py b/kallithea/lib/base.py --- a/kallithea/lib/base.py +++ b/kallithea/lib/base.py @@ -372,10 +372,13 @@ class BaseController(WSGIController): """ # Authenticate by API key - if api_key: - # when using API_KEY we are sure user exists. - return AuthUser(dbuser=User.get_by_api_key(api_key), - is_external_auth=True) + if api_key is not None: + au = AuthUser(dbuser=User.get_by_api_key(api_key), + authenticating_api_key=api_key, is_external_auth=True) + if au.is_anonymous: + log.warning('API key ****%s is NOT valid', api_key[-4:]) + raise webob.exc.HTTPForbidden(_('Invalid API key')) + return au # Authenticate by session cookie # In ancient login sessions, 'authuser' may not be a dict. diff --git a/kallithea/tests/functional/test_login.py b/kallithea/tests/functional/test_login.py --- a/kallithea/tests/functional/test_login.py +++ b/kallithea/tests/functional/test_login.py @@ -465,10 +465,10 @@ class TestLoginController(TestController @parametrize('test_name,api_key,code', [ ('none', None, 302), - ('empty_string', '', 302), - ('fake_number', '123456', 302), - ('fake_not_alnum', 'a-z', 302), - ('fake_api_key', '0123456789abcdef0123456789ABCDEF01234567', 302), + ('empty_string', '', 403), + ('fake_number', '123456', 403), + ('fake_not_alnum', 'a-z', 403), + ('fake_api_key', '0123456789abcdef0123456789ABCDEF01234567', 403), ('proper_api_key', True, 200) ]) def test_access_whitelisted_page_via_api_key(self, test_name, api_key, code): @@ -496,4 +496,4 @@ class TestLoginController(TestController #patch the API key and make it expired new_api_key.expires = 0 Session().commit() - self._api_key_test(new_api_key.api_key, status=302) + self._api_key_test(new_api_key.api_key, status=403)