Skip to content
Snippets Groups Projects
Verified Commit 01f966b0 authored by Timm Fitschen's avatar Timm Fitschen
Browse files

EHN: add unauthenticated as Authenticator implementation

parent 19064013
No related branches found
No related tags found
No related merge requests found
# -*- coding: utf-8 -*- #! -*- coding: utf-8 -*-
# #
# ** header v3.0 # ** header v3.0
# This file is a part of the CaosDB Project. # This file is a part of the CaosDB Project.
...@@ -25,10 +25,11 @@ ...@@ -25,10 +25,11 @@
# #
"""auth_token. """auth_token.
A Authentictor which only uses only a pre-supplied authentication token. An Authentictor which only uses only a pre-supplied authentication token.
""" """
from __future__ import absolute_import, unicode_literals, print_function from __future__ import absolute_import, unicode_literals, print_function
from .interface import AbstractAuthenticator, CaosDBServerConnection from .interface import AbstractAuthenticator, CaosDBServerConnection
from caosdb.connection.utils import auth_token_to_cookie
from caosdb.exceptions import LoginFailedException from caosdb.exceptions import LoginFailedException
...@@ -79,7 +80,9 @@ class AuthTokenAuthenticator(AbstractAuthenticator): ...@@ -79,7 +80,9 @@ class AuthTokenAuthenticator(AbstractAuthenticator):
def _logout(self): def _logout(self):
self.logger.debug("[LOGOUT]") self.logger.debug("[LOGOUT]")
if self.auth_token is not None: if self.auth_token is not None:
self._connection.request(method="DELETE", path="logout") headers = {'Cookie': auth_token_to_cookie(self.auth_token)}
self._connection.request(method="DELETE", path="logout",
headers=headers)
self.auth_token = None self.auth_token = None
def configure(self, **config): def configure(self, **config):
......
#! -*- coding: utf-8 -*-
#
# ** header v3.0
# This file is a part of the CaosDB Project.
#
# Copyright (C) 2018 Research Group Biomedical Physics,
# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
# Copyright (C) 2020 Timm Fitschen <f.fitschen@indiscale.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# ** end header
#
"""unauthenticated.
An Authentictor which suppresses any authentication and also ignores auth_token
cookies.
"""
from __future__ import absolute_import, unicode_literals, print_function
from .interface import AbstractAuthenticator, CaosDBServerConnection
from caosdb.exceptions import LoginFailedException
def get_authentication_provider():
"""get_authentication_provider.
Return an authenticator which only uses a pre-supplied authentication
token.
Returns
-------
AuthTokenAuthenticator
"""
return Unauthenticated()
class Unauthenticated(AbstractAuthenticator):
"""Unauthenticated.
Subclass of AbstractAuthenticator which suppresses any authentication and
also ignores auth_token cookies.
Methods
-------
login
logout
configure
on_request
on_response
"""
def __init__(self):
super(Unauthenticated, self).__init__()
self.auth_token = None
self._connection = None
def login(self):
self._login()
def _login(self):
raise LoginFailedException("This caosdb client is configured to stay "
"unauthenticated. Change your "
"`password_method` and provide an "
"`auth_token` or credentials if your want "
"to authenticate this client.")
def logout(self):
self._logout()
def _logout(self):
self.auth_token = None
def configure(self, **config):
self.auth_token = None
def on_request(self, method, path, headers, **kwargs):
# pylint: disable=unused-argument
"""on_request.
This implementation does not attempt to login or authenticate in any
form.
Parameters
----------
method
unused
path
unused
headers
unused
**kwargs
unused
"""
pass
def on_response(self, response):
# pylint: disable=unused-argument
"""on_response.
This implementation ignores any auth_token cookie sent by the server.
Parameters
----------
response
unused
"""
pass
...@@ -262,8 +262,8 @@ def _get_authenticator(**config): ...@@ -262,8 +262,8 @@ def _get_authenticator(**config):
---------- ----------
password_method : str password_method : str
The simple name of a submodule of caosdb.connection.authentication. The simple name of a submodule of caosdb.connection.authentication.
Currently, there are three valid values for this parameter: 'plain', Currently, there are four valid values for this parameter: 'plain',
'pass', and 'keyring'. 'pass', 'keyring' and 'auth_token'.
**config : **config :
Any other keyword arguments are passed the configre method of the Any other keyword arguments are passed the configre method of the
password_method. password_method.
...@@ -293,7 +293,8 @@ def _get_authenticator(**config): ...@@ -293,7 +293,8 @@ def _get_authenticator(**config):
except ImportError: except ImportError:
raise ConfigurationException("Password method \"{}\" not implemented. " raise ConfigurationException("Password method \"{}\" not implemented. "
"Valid methods: plain, pass, or keyring." "Try `plain`, `pass`, `keyring`, or "
"`auth_token`."
.format(config["password_method"])) .format(config["password_method"]))
...@@ -325,6 +326,7 @@ def configure_connection(**kwargs): ...@@ -325,6 +326,7 @@ def configure_connection(**kwargs):
- "input" Asks for the password. - "input" Asks for the password.
- "pass" Uses the `pass` password manager. - "pass" Uses the `pass` password manager.
- "keyring" Uses the `keyring` library. - "keyring" Uses the `keyring` library.
- "auth_token" Uses only a given auth_token.
timeout : int timeout : int
A connection timeout in seconds. (Default: 210) A connection timeout in seconds. (Default: 210)
...@@ -333,8 +335,9 @@ def configure_connection(**kwargs): ...@@ -333,8 +335,9 @@ def configure_connection(**kwargs):
Whether SSL certificate warnings should be ignored. Only use this for Whether SSL certificate warnings should be ignored. Only use this for
development purposes! (Default: False) development purposes! (Default: False)
auth_token : str auth_token : str (optional)
An authentication token which has been issued by the CaosDB Server. An authentication token which has been issued by the CaosDB Server.
Implies `password_method="auth_token"` if set.
implementation : CaosDBServerConnection implementation : CaosDBServerConnection
The class which implements the connection. (Default: The class which implements the connection. (Default:
...@@ -469,6 +472,9 @@ class _Connection(object): # pylint: disable=useless-object-inheritance ...@@ -469,6 +472,9 @@ class _Connection(object): # pylint: disable=useless-object-inheritance
"or a factory).", type_err) "or a factory).", type_err)
self._delegate_connection.configure(**config) self._delegate_connection.configure(**config)
if "auth_token" in config:
# deprecated, needed for older scripts
config["password_method"] = "auth_token"
if "password_method" not in config: if "password_method" not in config:
raise ConfigurationException("Missing password_method. You did " raise ConfigurationException("Missing password_method. You did "
"not specify a `password_method` for" "not specify a `password_method` for"
......
...@@ -284,6 +284,14 @@ USAGE ...@@ -284,6 +284,14 @@ USAGE
formatter_class=RawDescriptionHelpFormatter) formatter_class=RawDescriptionHelpFormatter)
parser.add_argument('-V', '--version', action='version', parser.add_argument('-V', '--version', action='version',
version=program_version_message) version=program_version_message)
parser.add_argument("--auth-token", metavar="AUTH_TOKEN",
dest="auth_token",
help=("A CaosDB authentication token (default: None). "
"If the authentication token is passed, the "
"`password_method` of the connection is set to "
"`auth_token` and the respective configuration "
"from the pycaosdb.ini is effectively being "
"overridden."))
subparsers = parser.add_subparsers( subparsers = parser.add_subparsers(
title="commands", title="commands",
metavar="COMMAND", metavar="COMMAND",
...@@ -600,8 +608,12 @@ USAGE ...@@ -600,8 +608,12 @@ USAGE
# Process arguments # Process arguments
args = parser.parse_args() args = parser.parse_args()
auth_token = args.auth_token
db.configure_connection()._login() if auth_token is not None:
db.configure_connection(password_method = "auth_token",
auth_token = auth_token)
else:
db.configure_connection()
return args.call(args) return args.call(args)
......
# -*- coding: utf-8 -*-
#
# ** header v3.0
# This file is a part of the CaosDB Project.
#
# Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# ** end header
#
"""test_authentication_auth_token
Unit tests for the module caosdb.connection.authentication.auth_token
"""
from __future__ import unicode_literals
from pytest import raises
from unittest.mock import Mock
from caosdb.connection.authentication import auth_token as at
from caosdb.connection.mockup import MockUpServerConnection, MockUpResponse
from caosdb.connection.utils import parse_auth_token
from caosdb.exceptions import LoginFailedException
from caosdb import configure_connection
def test_get_authentication_provider():
ap = at.get_authentication_provider()
assert isinstance(ap, at.AuthTokenAuthenticator)
def response_with_auth_token():
token = "SessionToken=[response token];"
assert parse_auth_token(token) is not None, "cookie not ok"
return MockUpResponse(200, {"Set-Cookie": token}, "ok")
def test_configure_connection():
def request_has_auth_token(**kwargs):
"""test resources"""
cookie = kwargs["headers"]["Cookie"]
assert cookie is not None
assert cookie == "SessionToken=%5Brequest%20token%5D;"
return response_with_auth_token()
c = configure_connection(password_method="auth_token",
auth_token="[request token]",
implementation=MockUpServerConnection)
assert isinstance(c._authenticator, at.AuthTokenAuthenticator)
c._delegate_connection.resources.append(request_has_auth_token)
assert c._authenticator.auth_token == "[request token]"
response = c._http_request(method="GET", path="test")
assert response.read() == "ok"
assert c._authenticator.auth_token == "[response token]"
def test_login_raises():
c = configure_connection(password_method="auth_token",
auth_token="[auth_token]")
with raises(LoginFailedException):
c._login()
def test_logout_calls_delete():
mock = Mock()
def logout_resource(**kwargs):
"""logout with auth_token"""
mock.method()
assert kwargs["path"] == "logout"
assert kwargs["method"] == "DELETE"
cookie = kwargs["headers"]["Cookie"]
assert cookie is not None
assert cookie == "SessionToken=%5Brequest%20token%5D;"
return MockUpResponse(200, {}, "ok")
c = configure_connection(password_method="auth_token",
auth_token="[request token]",
implementation=MockUpServerConnection)
c._delegate_connection.resources.append(logout_resource)
c._logout()
mock.method.assert_called_once()
# -*- coding: utf-8 -*-
#
# ** header v3.0
# This file is a part of the CaosDB Project.
#
# Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# ** end header
#
"""test_authentication_unauthenticated
Unit tests for the module caosdb.connection.authentication.unauthenticated.
"""
from __future__ import unicode_literals
from pytest import raises
from unittest.mock import Mock
from caosdb.connection.authentication import unauthenticated
from caosdb.connection.mockup import MockUpServerConnection, MockUpResponse
from caosdb.connection.utils import parse_auth_token
from caosdb.exceptions import LoginFailedException
from caosdb import configure_connection
from .test_authentication_auth_token import response_with_auth_token
def test_get_authentication_provider():
ap = unauthenticated.get_authentication_provider()
assert isinstance(ap, unauthenticated.Unauthenticated)
def test_configure_connection():
mock = Mock()
def request_has_no_auth_token(**kwargs):
"""test resource"""
assert "Cookie" not in kwargs["headers"]
mock.method()
return response_with_auth_token()
c = configure_connection(password_method="unauthenticated",
implementation=MockUpServerConnection)
assert isinstance(c._authenticator, unauthenticated.Unauthenticated)
c._delegate_connection.resources.append(request_has_no_auth_token)
assert c._authenticator.auth_token is None
response = c._http_request(method="GET", path="test")
assert response.read() == "ok"
mock.method.assert_called_once()
assert c._authenticator.auth_token is None
def test_login_raises():
c = configure_connection(password_method="unauthenticated")
with raises(LoginFailedException):
c._login()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment