Skip to content
Snippets Groups Projects
Unverified Commit 06327226 authored by Timm Fitschen's avatar Timm Fitschen
Browse files

ENH: set server properties

parent a50b79f5
No related branches found
No related tags found
No related merge requests found
...@@ -32,8 +32,8 @@ setup(name='PyCaosDB', ...@@ -32,8 +32,8 @@ setup(name='PyCaosDB',
author_email='timm.fitschen@ds.mpg.de', author_email='timm.fitschen@ds.mpg.de',
packages=find_packages('src'), packages=find_packages('src'),
package_dir={'': 'src'}, package_dir={'': 'src'},
install_requires=['lxml>=3.6.4', 'coverage>=4.4.2', install_requires=['lxml>=3.6.4',
'PyYaml>=3.12', 'future'], 'PyYaml>=3.12', 'future'],
extras_require={'keyring': ['keyring>=13.0.0']}, extras_require={'keyring': ['keyring>=13.0.0']},
tests_require=["pytest"], tests_require=["pytest", "pytest-cov", "coverage>=4.4.2"],
) )
...@@ -42,5 +42,7 @@ from caosdb.common.models import (delete, execute_query, raise_errors, ...@@ -42,5 +42,7 @@ from caosdb.common.models import (delete, execute_query, raise_errors,
get_global_acl, get_known_permissions) get_global_acl, get_known_permissions)
# Import of convenience methods: # Import of convenience methods:
import caosdb.apiutils import caosdb.apiutils
# read configuration these files
configure(expanduser('~/.pycaosdb.ini')) configure(expanduser('~/.pycaosdb.ini'))
configure(join(getcwd(), "pycaosdb.ini")) configure(join(getcwd(), "pycaosdb.ini"))
...@@ -31,6 +31,69 @@ from caosdb.connection.connection import get_connection ...@@ -31,6 +31,69 @@ from caosdb.connection.connection import get_connection
from caosdb.common.utils import xml2str from caosdb.common.utils import xml2str
def set_server_property(key, value):
"""set_server_property
Set a server property.
Parameters
----------
key : str
The name of the server property.
value : str
The value of the server property.
Returns
-------
None
"""
con = get_connection()
con._form_data_request(method="POST", path="_server_properties",
params={key: value}).read()
def get_server_properties():
"""get_server_properties
Get all server properties as a dict.
Returns
-------
dict
The server properties.
"""
con = get_connection()
body = con._http_request(method="GET", path="_server_properties").response
xml = etree.parse(body)
props = dict()
for elem in xml.getroot():
props[elem.tag] = elem.text
return props
def get_server_property(key):
"""get_server_property
Get a server property.
Parameters
----------
key : str
The name of the server property
Returns
-------
value : str
The string value of the server property.
Raises
------
KeyError
If the server property is no defined.
"""
return get_server_properties()[key]
def _retrieve_user(name, realm=None, **kwargs): def _retrieve_user(name, realm=None, **kwargs):
con = get_connection() con = get_connection()
try: try:
......
...@@ -21,9 +21,6 @@ ...@@ -21,9 +21,6 @@
# #
# ** end header # ** end header
# #
"""Created on 20.09.2016."""
try: try:
# python2 # python2
from ConfigParser import ConfigParser from ConfigParser import ConfigParser
...@@ -31,29 +28,9 @@ except ImportError: ...@@ -31,29 +28,9 @@ except ImportError:
# python3 # python3
from configparser import ConfigParser from configparser import ConfigParser
_DEFAULTS = {"Connection":
{"url": None,
"timeout": "200",
"username": None,
"password_method": "plain",
"debug": "0",
"cacert": None},
"Container":
{"debug": "0"}}
def _reset_config(): def _reset_config():
global _pycaosdbconf global _pycaosdbconf
pycaosdbconf = None _pycaosdbconf = ConfigParser(allow_no_value=False)
_pycaosdbconf = ConfigParser(allow_no_value=True)
_init_defaults(_pycaosdbconf)
def _init_defaults(confpar):
for sec in _DEFAULTS.keys():
confpar.add_section(sec)
for opt in _DEFAULTS[sec].keys():
confpar.set(sec, opt, _DEFAULTS[sec][opt])
def configure(inifile): def configure(inifile):
......
...@@ -72,6 +72,13 @@ class _WrappedHTTPResponse(CaosDBHTTPResponse): ...@@ -72,6 +72,13 @@ class _WrappedHTTPResponse(CaosDBHTTPResponse):
class _DefaultCaosDBServerConnection(CaosDBServerConnection): class _DefaultCaosDBServerConnection(CaosDBServerConnection):
"""_DefaultCaosDBServerConnection
Methods
-------
configure
request
"""
def __init__(self): def __init__(self):
self._useragent = ("PyCaosDB - " self._useragent = ("PyCaosDB - "
"DefaultCaosDBServerConnection") "DefaultCaosDBServerConnection")
...@@ -79,6 +86,28 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection): ...@@ -79,6 +86,28 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection):
self._base_path = None self._base_path = None
def request(self, method, path, headers=None, body=None, **kwargs): def request(self, method, path, headers=None, body=None, **kwargs):
"""request
Send a HTTP request to the server.
Parameters
----------
method : str
The HTTP request method.
path : str
An URI path segment (without the 'scheme://host:port/' parts),
including query and frament segments.
headers : dict of str -> str, optional
HTTP request headers. (Defautl: None)
body : str or bytes or readable, opional
The body of the HTTP request. Bytes should be a utf-8 encoded
string.
**kwargs :
Any keyword arguments will be ignored.
Returns
-------
"""
if headers is None: if headers is None:
headers = {} headers = {}
try: try:
...@@ -91,6 +120,27 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection): ...@@ -91,6 +120,27 @@ class _DefaultCaosDBServerConnection(CaosDBServerConnection):
return _WrappedHTTPResponse(self._http_con.getresponse()) return _WrappedHTTPResponse(self._http_con.getresponse())
def configure(self, **config): def configure(self, **config):
"""configure
Configure the http connection.
Parameters
----------
cacert : str
Path to the CA certificate which will be used to identify the
server.
url : str
The url of the CaosDB Server, e.g.
`https://example.com:443/rootpath`, including a possible root path.
**config :
Any further keyword arguments are being ignored.
Raises
------
ConnectionException
If no url has been specified, or if the CA certificate cannot be
loaded.
"""
context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) context = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
context.verify_mode = ssl.CERT_REQUIRED context.verify_mode = ssl.CERT_REQUIRED
if hasattr(context, "check_hostname"): if hasattr(context, "check_hostname"):
...@@ -141,10 +191,36 @@ def _make_conf(*conf): ...@@ -141,10 +191,36 @@ def _make_conf(*conf):
_DEFAULT_CONF = {"password_method": "plain", "implementation": _DEFAULT_CONF = {"password_method": "plain", "implementation":
_DefaultCaosDBServerConnection} _DefaultCaosDBServerConnection, "timeout": 210}
def _get_authenticator(**config): def _get_authenticator(**config):
"""_get_authenticator
Import and configure the password_method.
Parameters
----------
password_method : str
The simple name of a submodule of caosdb.connection.authentication.
Currently, there are three valid values for this parameter: 'plain',
'pass', and 'keyring'.
**config :
Any other keyword arguments are passed the configre method of the
password_method.
Returns
-------
AbstractAuthenticator
An object which implements the password_method and which already
configured.
Raises
------
ConnectionException
If the password_method string cannot be resolved to a CaosAuthenticator
class.
"""
auth_module = ("caosdb.connection.authentication." + auth_module = ("caosdb.connection.authentication." +
config["password_method"]) config["password_method"])
_LOGGER.debug("import auth_module %s", auth_module) _LOGGER.debug("import auth_module %s", auth_module)
...@@ -156,29 +232,37 @@ def _get_authenticator(**config): ...@@ -156,29 +232,37 @@ def _get_authenticator(**config):
return auth_provider return auth_provider
except ImportError: except ImportError:
raise RuntimeError("Password method \"{}\" not implemented. " raise ConfigurationException("Password method \"{}\" not implemented. "
"Valid methods: plain, pass, or keyring." "Valid methods: plain, pass, or keyring."
.format(config["password_method"])) .format(config["password_method"]))
def configure_connection(**kwargs): def configure_connection(**kwargs):
"""Configures the caosdb connection. """Configures the caosdb connection and return the Connection object.
All paramters are optional. The default configuration is taken from the The effective configuration is governed by the default values (see
`Connection` section of the configuration which can be retrieved by 'Parameters'), the global configuration (see `caosdb.get_config()`) and the
`get_config` parameters which are passed to this function, with ascending priority.
Returns the Connection object. The parameters which are listed here, are possibly not sufficient for a
working configuration of the connection. Check the `configure` method of
Typical arguments are: the implementation class and the password_method for more details.
url The URL of the CaosDB server. E.g. https://dumiatis01:8433/playground
username The username Parameters
password Two options: ----------
1) The plain text password implementation : CaosDBServerConnection
2) A function which returns the password. The class which implements the connection. (Default:
timeout A connection timeout in seconds.
implementation A class which implements CaosDBServerConnection. (Default:
_DefaultCaosDBServerConnection) _DefaultCaosDBServerConnection)
password_method : str
The name of a submodule of caosdb.connection.authentication which
implements the AbstractAuthenticator interface. (Default: 'plain')
timeout : int
A connection timeout in seconds. (Default: 210)
Returns
-------
_Connection
The singleton instance of the _Connection class.
""" """
global_conf = (dict(get_config().items("Connection")) if global_conf = (dict(get_config().items("Connection")) if
get_config().has_section("Connection") else dict()) get_config().has_section("Connection") else dict())
...@@ -237,7 +321,8 @@ def _handle_response_status(http_response): ...@@ -237,7 +321,8 @@ def _handle_response_status(http_response):
class _Connection(object): # pylint: disable=useless-object-inheritance class _Connection(object): # pylint: disable=useless-object-inheritance
"""This connection class provides the interface to the database connection """This connection class provides the interface to the database connection
allowing for retrieval, insertion, update, etc. of entries. allowing for retrieval, insertion, update, etc. of entities, files, users,
roles and much more.
It wrapps an instance of CaosDBServerConnection which actually does the It wrapps an instance of CaosDBServerConnection which actually does the
work (how, depends on the instance). work (how, depends on the instance).
......
...@@ -47,7 +47,7 @@ class MockUpResponse(CaosDBHTTPResponse): ...@@ -47,7 +47,7 @@ class MockUpResponse(CaosDBHTTPResponse):
def __init__(self, status, headers, body): def __init__(self, status, headers, body):
self._status = status self._status = status
self.headers = headers self.headers = headers
self.body = StringIO(body) self.response = StringIO(body)
@property @property
def status(self): def status(self):
...@@ -56,7 +56,7 @@ class MockUpResponse(CaosDBHTTPResponse): ...@@ -56,7 +56,7 @@ class MockUpResponse(CaosDBHTTPResponse):
def read(self, size=-1): def read(self, size=-1):
"""Return the body of the response.""" """Return the body of the response."""
return self.body.read(size) return self.response.read(size)
def getheader(self, name, default=None): def getheader(self, name, default=None):
"""Get the contents of the header `name`, or `default` if there is no """Get the contents of the header `name`, or `default` if there is no
......
# -*- encoding: 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
#
# 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
#
"""Tests for the administration class."""
# pylint: disable=missing-docstring
from __future__ import unicode_literals
from pytest import raises
from caosdb import administration, configure_connection, get_connection
from caosdb.connection.mockup import MockUpServerConnection, MockUpResponse
def setup():
configure_connection(url="unittests", username="testuser",
password="testpassword", timeout=200,
implementation=MockUpServerConnection)
def test_get_server_properties_success():
properties = "<Properties><TEST_PROP>TEST_VAL</TEST_PROP></Properties>"
get_connection()._delegate_connection.resources.append(
lambda **kwargs: MockUpResponse(200, {}, properties))
props = administration.get_server_properties()
assert isinstance(props, dict)
def test_get_server_property_success():
properties = "<Properties><TEST_PROP>TEST_VAL</TEST_PROP></Properties>"
get_connection()._delegate_connection.resources.append(
lambda **kwargs: MockUpResponse(200, {}, properties))
assert "TEST_VAL" == administration.get_server_property("TEST_PROP")
def test_get_server_property_key_error():
properties = "<Properties><TEST_PROP>TEST_VAL</TEST_PROP></Properties>"
get_connection()._delegate_connection.resources.append(
lambda **kwargs: MockUpResponse(200, {}, properties))
with raises(KeyError) as e:
assert administration.get_server_property("BLA")
def test_set_server_property():
def check_form(**kwargs):
assert kwargs["path"] == "_server_properties"
assert kwargs["method"] == "POST"
assert kwargs["body"] == "TEST_PROP=TEST_VAL".encode()
assert kwargs["headers"]["Content-Type"] == "application/x-www-form-urlencoded"
return MockUpResponse(200, {}, "<Properties><TEST_PROP>TEST_VAL</TEST_PROP></Properties>")
get_connection()._delegate_connection.resources.append(check_form)
administration.set_server_property("TEST_PROP", "TEST_VAL")
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment