Skip to content

Commit

Permalink
Merge pull request #61 from EUDAT-B2SAFE/devel
Browse files Browse the repository at this point in the history
Transfer changes for 1.0.1 to master
  • Loading branch information
TobiasWeigel committed Apr 12, 2016
2 parents 39f4430 + 79c3fca commit 121530c
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 34 deletions.
1 change: 1 addition & 0 deletions b2handle/handleclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,7 @@ def register_handle(self, handle, location, checksum=None, additional_URLs=None,
handlerecord_json = self.retrieve_handle_record_json(handle)
if handlerecord_json is not None:
msg = 'Could not register handle'
LOGGER.error(msg+', as it already exists.')
raise HandleAlreadyExistsException(handle=handle, msg=msg)

# Create admin entry
Expand Down
33 changes: 29 additions & 4 deletions b2handle/handlesystemconnector.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import hsresponses
import util
import utilhandle
import utilconfig

LOGGER = logging.getLogger(__name__)
LOGGER.addHandler(util.NullHandler())
Expand Down Expand Up @@ -78,6 +79,7 @@ def __init__(self, **args):
self.__authentication_method = None
self.__session = requests.Session()
self.__no_auth_message = 'No credentials passed. Read access only.'
self.__first_request = True

# Needed for read and write access:
self.__store_args_or_set_to_defaults(args, defaults)
Expand Down Expand Up @@ -114,7 +116,9 @@ def __store_args_or_set_to_defaults(self, args, defaults):


if args['HTTPS_verify'] is not None:
self.__HTTPS_verify = util.string_to_bool(args['HTTPS_verify'])
self.__HTTPS_verify = utilconfig.get_valid_https_verify(
args['HTTPS_verify']
)
LOGGER.info(' - https_verify set to: '+str(self.__HTTPS_verify))
else:
self.__HTTPS_verify = defaults['HTTPS_verify']
Expand Down Expand Up @@ -264,11 +268,23 @@ def send_handle_get_request(self, handle, indices=None):
:return: The server's response.
'''


# Assemble required info:
url = self.make_handle_URL(handle, indices)
LOGGER.debug('GET Request to '+url)
head = self.__get_headers('GET')
veri = self.__HTTPS_verify
resp = self.__session.get(url, headers=head, verify=veri)

# Send the request
if self.__cert_needed_for_get_request():
# If this is the first request and the connector uses client cert authentication, we need to send the cert along
# in the first request that builds the session.
resp = self.__session.get(url, headers=head, verify=veri, cert=self.__cert_object)
else:
# Normal case:
resp = self.__session.get(url, headers=head, verify=veri)

# Log and return
self.__log_request_response_to_file(
logger=REQUESTLOGGER,
op='GET',
Expand All @@ -278,8 +294,16 @@ def send_handle_get_request(self, handle, indices=None):
verify=veri,
resp=resp
)
self.__first_request = False
return resp

def __cert_needed_for_get_request(self):
if (self.__first_request) and (self.__has_write_access) and (self.__authentication_method == self.__auth_methods['cert']):
return True
else:
return False


def send_handle_put_request(self, **args):
'''
Send a HTTP PUT request to the handle server to write either an entire
Expand Down Expand Up @@ -332,6 +356,7 @@ def send_handle_put_request(self, **args):
payload = json.dumps({'values':list_of_entries})
LOGGER.debug('PUT Request payload: '+payload)
head = self.__get_headers('PUT')
LOGGER.debug('PUT Request headers: '+str(head))
veri = self.__HTTPS_verify

# Make request:
Expand All @@ -358,7 +383,7 @@ def send_handle_put_request(self, **args):
response=resp,
username=self.__username
)

self.__first_request = False
return resp, payload

def send_handle_delete_request(self, **args):
Expand Down Expand Up @@ -421,7 +446,7 @@ def send_handle_delete_request(self, **args):
response=resp,
username=self.__username
)

self.__first_request = False
return resp

def check_if_username_exists(self, username):
Expand Down
5 changes: 4 additions & 1 deletion b2handle/searcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import json
import utilhandle
import util
import utilconfig
from handleexceptions import ReverseLookupException

LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -91,7 +92,9 @@ def __store_args_or_set_to_defaults(self, args, defaults):
LOGGER.debug('Setting the attributes:')

if args['HTTPS_verify'] is not None: # Without this check, a passed "False" is not found!
self.__HTTPS_verify = util.string_to_bool(args['HTTPS_verify'])
self.__HTTPS_verify = utilconfig.get_valid_https_verify(
args['HTTPS_verify']
)
LOGGER.info(' - https_verify set to: '+str(self.__HTTPS_verify))
else:
self.__HTTPS_verify = defaults['HTTPS_verify']
Expand Down
2 changes: 1 addition & 1 deletion b2handle/tests/handleclient_writeaccess_patched_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def test_register_handle_different_owner(self, getpatch, putpatch, username_chec
passed_payload, _ = self.get_payload_headers_from_mockresponse(putpatch)

# Compare with expected payload:
expected_payload = {"values": [{"index": 100, "type": "HS_ADMIN", "data": {"value": {"index": "300", "handle": "handle/owner", "permissions": "011111110011"}, "format": "admin"}}, {"index": 1, "type": "URL", "data": "http://foo.bar"}, {"index": 2, "type": "CHECKSUM", "data": "123456"}, {"index": 3, "type": "FOO", "data": "foo"}, {"index": 4, "type": "BAR", "data": "bar"}, {"index": 5, "type": "10320/LOC", "data": "<locations><location href=\"http://bar.bar\" id=\"0\" /><location href=\"http://foo.foo\" id=\"1\" /></locations>"}]}
expected_payload = {"values": [{"index": 100, "type": "HS_ADMIN", "data": {"value": {"index": 300, "handle": "handle/owner", "permissions": "011111110011"}, "format": "admin"}}, {"index": 1, "type": "URL", "data": "http://foo.bar"}, {"index": 2, "type": "CHECKSUM", "data": "123456"}, {"index": 3, "type": "FOO", "data": "foo"}, {"index": 4, "type": "BAR", "data": "bar"}, {"index": 5, "type": "10320/LOC", "data": "<locations><location href=\"http://bar.bar\" id=\"0\" /><location href=\"http://foo.foo\" id=\"1\" /></locations>"}]}
replace_timestamps(expected_payload)
self.assertEqual(passed_payload, expected_payload,
failure_message(expected=expected_payload, passed=passed_payload, methodname='register_handle'))
Expand Down
5 changes: 5 additions & 0 deletions b2handle/tests/main_test_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from clientcredentials_test import PIDClientCredentialsTestCase
from handleclient_search_noaccess_test import EUDATHandleClientSearchNoAccessTestCase
from handleconnector_access_patched_test import EUDATHandleConnectorAccessPatchedTestCase
from utilconfig_test import UtilConfigTestCase

# Integration tests:
# Imports below!
Expand Down Expand Up @@ -82,6 +83,10 @@
tests_to_run = []
numtests = 0

utilconfig_testcase = unittest.TestLoader().loadTestsFromTestCase(UtilConfigTestCase)
tests_to_run.append(utilconfig_testcase)
numtests += utilconfig_testcase.countTestCases()

if no_access:

noaccess = unittest.TestLoader().loadTestsFromTestCase(EUDATHandleClientNoaccessTestCase)
Expand Down
26 changes: 26 additions & 0 deletions b2handle/tests/utilconfig_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import sys
if sys.version_info < (2, 7):
import unittest2 as unittest
else:
import unittest

from b2handle.utilconfig import get_valid_https_verify


class UtilConfigTestCase(unittest.TestCase):

def test_valid_https_verify_bool_true(self):
"""Test return bool True when getting bool True"""
self.assertEqual(get_valid_https_verify(True), True)

def test_valid_https_verify_string_true(self):
"""Test return bool True when getting string True"""
self.assertEqual(get_valid_https_verify('True'), True)

def test_valid_https_verify_string_false(self):
"""Test return bool False when getting string False"""
self.assertEqual(get_valid_https_verify('False'), False)

def test_valid_https_verify_bool_string(self):
"""Test return string when getting a string value in https_verify"""
self.assertEqual(get_valid_https_verify('ca_cert.crt'), 'ca_cert.crt')
20 changes: 0 additions & 20 deletions b2handle/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,6 @@ def check_presence_of_mandatory_args(args, mandatory_args):
else:
return True

def string_to_bool(string):
'''
Parses a string to a boolean. Accepts the words
"true" and "false" in any mixture of capital
andnon-capital letters. If the word is neither
"true" nor "false", a KeyError is raised.
:string: The string to parse. Passing a boolean
does not harm.
:returns: True or False.
:raise: :exc:`~KeyError`
'''

dic = {'false':False, 'true':True}
if string is True or string is False:
return string
else:
return dic[string.lower()]

def log_instantiation(LOGGER, classname, args, forbidden, with_date=False):
'''
Log the instantiation of an object to the given logger.
Expand Down Expand Up @@ -120,4 +101,3 @@ def log_instantiation(LOGGER, classname, args, forbidden, with_date=False):
LOGGER.debug('Param '+argname+'*******')
else:
LOGGER.debug('Param '+argname+'='+str(args[argname]))

24 changes: 24 additions & 0 deletions b2handle/utilconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'''
This module provides functions to parse
and validate b2handle configuration
'''

def get_valid_https_verify(value):
'''
Get a value that can be the boolean representation of a string
or a boolean itself and returns It as a boolean.
If this is not the case, It returns a string.
:value: The HTTPS_verify input value. A string can be passed as a path
to a CA_BUNDLE certificate
:returns: True, False or a string.
'''
http_verify_value = value
bool_values = {'false': False, 'true': True}

if isinstance(value, bool):
http_verify_value = value
elif isinstance(value, str) and value.lower() in bool_values.keys():
http_verify_value = bool_values[value.lower()]

return http_verify_value
1 change: 1 addition & 0 deletions b2handle/utilhandle.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def remove_index_from_handle(handle_with_index):

split = handle_with_index.split(':')
if len(split) == 2:
split[0] = int(split[0])
return split
elif len(split) == 1:
return (None, handle_with_index)
Expand Down
27 changes: 22 additions & 5 deletions docs/source/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ Creating the client certificate
.. code:: json
bash /.../handlesystem_software/hsj-8.x.x/bin/hdl-keygen
-alg dsa
-keysize 2048
-alg rsa
-keysize 4096
301_foo_bar_privkey.bin 301_foo_bar_pubkey.bin
Note: We put 301_foo_bar into the name to remember for which username this keypair is generated!
Expand Down Expand Up @@ -102,14 +102,25 @@ Creating the client certificate
4. Creating the certificate file:

* This can be done using openssl:
* This can be done using openssl without specifying a subject:

.. code:: json
openssl req -pubkey -x509 -new -key /.../301_foo_bar_privkey.pem
-out /.../301_certificate_and_publickey.pem -sha256
* This can be done using openssl with specifying a subject:

.. code:: json
openssl req -pubkey -x509 -new -sha256 -subj "/CN=301:foo\/bar"
-key /.../301_foo_bar_privkey.pem
-out /.../301_certificate_and_publickey.pem
* The tool is then going to prompt for some information. For the first 5 prompts, it does not matter what you enter- the entries are going to be ignored by the Handle Server.
* The tool is then going to prompt for some information if you don not specify a subject. For the first 5 prompts, it does not matter what you enter- the entries are going to be ignored by the Handle Server.
However, it is very important to enter the username as Common Name and *leave the Email address blank*, as it is going to be appended to the username otherwise. This will look like
this:

Expand Down Expand Up @@ -247,11 +258,17 @@ HTTP 403
* Handle Server responseCode: 400 (*Other authentication errors*)
* HTTP status code 403 (*Forbidden*).

**Possible solution:**
**Possible solution 1:**

This error occurs if the username does not have admin permissions yet. Make sure it is referred to in a
HS_ADMIN or HS_VLIST that has admin permissions.

**Possible solution 2:**

This error also occurs if the username did not get permissions for this specific handle in its HS_ADMIN entry. Each user
can only modify handles whose HS_ADMIN entry (or one of its HS_ADMIN entries) gives write permissions to him, either directly
or by pointing to a HS_VLIST that has admin permissions and that contains the username.


Handshake Failure
-----------------
Expand Down
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@
# built documents.
#
# The short X.Y version.
version = '1.0'
version = '1.0.1'
# The full version, including alpha/beta/rc tags.
release = '1.0'
release = '1.0.1'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from setuptools import setup, find_packages
import sys, os

version = '1.0'
version = '1.0.1'

# Set common test dependencies
test_dependencies = [
Expand Down

0 comments on commit 121530c

Please sign in to comment.