diff --git a/README.md b/README.md index b659973..d2eced9 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ The b2handle Python library is a client library for interaction with a [Handle System](https://handle.net) server, using the native REST interface introduced in Handle System 8. The library offers methods to create, update and delete Handles as well as advanced functionality such as searching over Handles using an additional search servlet and managing multiple location entries per Handle. The library currently supports Python 2.6 and Python 2.7 and requires at least a Handle System server 8.1. +The library requires OpenSSL v1.0.1 or higher. # Installation and use diff --git a/b2handle/handleclient.py b/b2handle/handleclient.py index 7688884..249e794 100644 --- a/b2handle/handleclient.py +++ b/b2handle/handleclient.py @@ -61,8 +61,8 @@ def __init__(self, handle_server_url=None, **args): :param REST_API_url_extension: Optional. The extension of a Handle Server's URL to access its REST API. Defaults to '/api/handles/'. :param allowed_search_keys: Optional. The keys that can be used for - reverse lookup of handles, as a list of strings. Defaults to 'url' - and 'checksum'. If the list is empty, all keys are passed to the + reverse lookup of handles, as a list of strings. Defaults to 'URL' + and 'CHECKSUM'. If the list is empty, all keys are passed to the reverse lookup servlet and exceptions are passed on to the user. :param 10320LOC_chooseby: Optional. The value to give to a handle record's 10320/LOC entry's 'chooseby' attribute as string (e.g. @@ -70,8 +70,10 @@ def __init__(self, handle_server_url=None, **args): :param modify_HS_ADMIN: Optional. Advanced usage. Determines whether the HS_ADMIN handle record entry can be modified using this library. Defaults to False and should not be modified. - :param HTTPS_verify: Optional. If set to False, the certificate is not - verified in HTTP requests. Defaults to True. + :param HTTPS_verify: Optional. This parameter can have three values. + 'True', 'False' or 'the path to a CA_BUNDLE file or directory with + certificates of trusted CAs'. If set to False, the certificate is + not verified in HTTP requests. Defaults to True. :param reverselookup_baseuri: Optional. The base URL of the reverse lookup service. If not set, the handle server base URL is used. :param reverselookup_url_extension: Optional. The path to append to diff --git a/b2handle/searcher.py b/b2handle/searcher.py index 3b77b77..0cade45 100644 --- a/b2handle/searcher.py +++ b/b2handle/searcher.py @@ -201,6 +201,16 @@ def search_handle(self, **args): msg = 'No search terms have been specified. Please specify'+\ ' at least one key-value-pair.' raise ReverseLookupException(msg=msg) + else: + isnone = util.return_keys_of_value_none(args) + if len(isnone) > 0: + LOGGER.debug('search_handle: These keys had value None: '+str(isnone)) + args = util.remove_value_none_from_dict(args) + if len(args) == 0: + LOGGER.debug('search_handle: No key value pair with valid value was specified.') + msg = ('No search terms have been specified. Please specify' + ' at least one key-value-pair.') + raise ReverseLookupException(msg=msg) # Perform the search: list_of_handles = [] @@ -286,10 +296,10 @@ def create_revlookup_query(self, *fulltext_searchterms, **keyvalue_searchterms): only_search_for_allowed_keys = True fulltext_searchterms_given = True + fulltext_searchterms = util.remove_value_none_from_list(fulltext_searchterms) if len(fulltext_searchterms) == 0: fulltext_searchterms_given = False - if len(fulltext_searchterms) == 1 and fulltext_searchterms[0] is None: - fulltext_searchterms_given = False + if fulltext_searchterms_given: msg = 'Full-text search is not implemented yet.'+\ ' The provided searchterms '+str(fulltext_searchterms)+\ @@ -297,11 +307,9 @@ def create_revlookup_query(self, *fulltext_searchterms, **keyvalue_searchterms): raise ReverseLookupException(msg=msg) keyvalue_searchterms_given = True + keyvalue_searchterms = util.remove_value_none_from_dict(keyvalue_searchterms) if len(keyvalue_searchterms) == 0: keyvalue_searchterms_given = False - if len(keyvalue_searchterms) == 1 and\ - keyvalue_searchterms.itervalues().next() is None: - keyvalue_searchterms_given = False if not keyvalue_searchterms_given and not fulltext_searchterms_given: msg = 'No search terms have been specified. Please specify'+\ diff --git a/b2handle/tests/handleclient_search_noaccess_test.py b/b2handle/tests/handleclient_search_noaccess_test.py index 24d62d7..151d2d7 100644 --- a/b2handle/tests/handleclient_search_noaccess_test.py +++ b/b2handle/tests/handleclient_search_noaccess_test.py @@ -66,6 +66,26 @@ def test_create_revlookup_query_normal(self): self.assertEqual(query, '?URL=foo', 'The query is: '+query) + def test_create_revlookup_query_normal_checksum(self): + query = self.searcher.create_revlookup_query(CHECKSUM='foo') + self.assertEqual(query, '?CHECKSUM=foo', + 'The query is: '+query) + + def test_create_revlookup_query_normal_checksum_and_url(self): + query = self.searcher.create_revlookup_query(CHECKSUM='foo', URL='bar') + self.assertEqual(query, '?URL=bar&CHECKSUM=foo', + 'The query is: '+query) + + def test_create_revlookup_query_checksum_and_none_url(self): + query = self.searcher.create_revlookup_query(CHECKSUM='foo', URL=None) + self.assertEqual(query, '?URL=bar&CHECKSUM=foo', + 'The query is: '+query) + + def test_create_revlookup_query_checksum_and_none_url(self): + query = self.searcher.create_revlookup_query(CHECKSUM='foo', URL=None, something=None) + self.assertEqual(query, '?CHECKSUM=foo', + 'The query is: '+query) + def test_instantiate_wrong_search_url(self): inst = EUDATHandleClient.instantiate_for_read_and_search( diff --git a/b2handle/tests/handleclient_searchaccess_test.py b/b2handle/tests/handleclient_searchaccess_test.py index e587cc4..86baa4b 100644 --- a/b2handle/tests/handleclient_searchaccess_test.py +++ b/b2handle/tests/handleclient_searchaccess_test.py @@ -189,6 +189,27 @@ def test_search_handle_for_url_and_checksum(self): self.assertEqual(val1, [], 'val1 is: '+str(val1)+', instead of []') self.assertEqual(val2, [], 'val2 is: '+str(val2)+', instead of []') + def test_search_handle_for_checksum(self): + """Test searching for checksum with wildcards.""" + log_new_test_case("test_search_handle_for_checksum") + + log_start_test_code() + val1 = self.inst.search_handle(None, CHECKSUM='*1111111111111*') + + log_end_test_code() + log_start_test_code() + val2 = self.inst.search_handle(URL=None, CHECKSUM='*1111111111111*') + log_end_test_code() + + # Check desired outcome: + self.assertEqual(type(val1),type([]), + 'Searching did not return a list, but: '+str(val1)+', type: '+str(type(val1))) + self.assertEqual(val1, val2, + 'Searching with or without keyword did not return the same result:'+\ + '\nwith keyword: '+str(val1)+'\nwithout: '+str(val2)) + self.assertEqual(val1, [], 'val1 is: '+str(val1)+', instead of []') + self.assertEqual(val2, [], 'val2 is: '+str(val2)+', instead of []') + def test_search_handle_prefixfilter(self): """Test filtering for prefixes.""" log_new_test_case("test_search_handle_prefixfilter") diff --git a/b2handle/tests/utilconfig_test.py b/b2handle/tests/utilconfig_test.py index e316b9f..d161523 100644 --- a/b2handle/tests/utilconfig_test.py +++ b/b2handle/tests/utilconfig_test.py @@ -21,6 +21,14 @@ 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_unicode_string_true(self): + """Test return bool True when getting unicode string True""" + self.assertEqual(get_valid_https_verify(u'True'), True) + + def test_valid_https_verify_unicode_string_false(self): + """Test return bool False when getting unicode string False""" + self.assertEqual(get_valid_https_verify(u'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') diff --git a/b2handle/util.py b/b2handle/util.py index 93a420a..edfe11b 100644 --- a/b2handle/util.py +++ b/b2handle/util.py @@ -73,6 +73,35 @@ def check_presence_of_mandatory_args(args, mandatory_args): else: return True +def return_keys_of_value_none(dictionary): + isnone = [] + for key,value in dictionary.iteritems(): + if value is None: + isnone.append(key) + return isnone + +def remove_value_none_from_dict(dictionary): + isnone = return_keys_of_value_none(dictionary) + if len(isnone) > 0: + for nonekey in isnone: + dictionary.pop(nonekey) + return dictionary + +def return_indices_of_value_none(mylist): + isnone = [] + for i in xrange(len(mylist)): + if mylist[i] is None: + isnone.append(i) + return isnone + +def remove_value_none_from_list(mylist): + isnone = return_indices_of_value_none(mylist) + if len(isnone) > 0: + for index in isnone: + mylist.pop(index) + return mylist + + def log_instantiation(LOGGER, classname, args, forbidden, with_date=False): ''' Log the instantiation of an object to the given logger. diff --git a/b2handle/utilconfig.py b/b2handle/utilconfig.py index c5992f3..361684f 100644 --- a/b2handle/utilconfig.py +++ b/b2handle/utilconfig.py @@ -18,7 +18,7 @@ def get_valid_https_verify(value): if isinstance(value, bool): http_verify_value = value - elif isinstance(value, str) and value.lower() in bool_values.keys(): + elif (isinstance(value, str) or isinstance(value, unicode)) and value.lower() in bool_values.keys(): http_verify_value = bool_values[value.lower()] return http_verify_value diff --git a/docs/Dockerfile b/docs/Dockerfile index 0d9b878..610d566 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -7,7 +7,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ && rm -rf /var/lib/apt/lists/* RUN easy_install pip -RUN pip install sphinx +RUN pip install \ + sphinx \ + sphinx_rtd_theme VOLUME /opt/B2HANDLE/docs diff --git a/docs/source/authentication.rst b/docs/source/authentication.rst index 8af08a4..392786a 100644 --- a/docs/source/authentication.rst +++ b/docs/source/authentication.rst @@ -26,7 +26,7 @@ Index Key Value ... ... ... ===== ========= ========== -The handle server admin has to add the ``HS_SECKEY`` entry with the user's password to an existing handle (e.g. '/allusers') or create a new handle for this purpose (e.g. '/johndoe'. He or she also has to grant write permissions to the user (see below). +The handle server admin has to add the ``HS_SECKEY`` entry with the user's password to an existing handle (e.g. '/allusers') or create a new handle for this purpose (e.g. '/johndoe'). He or she also has to grant write permissions to the user (see below). Using client certificates @@ -43,204 +43,12 @@ Index Key Value ... ... ... ===== ========= ============== -The handle server admin has to add the ``HS_PUBKEY`` entry with the user's public key to an existing handle or create a new handle for this purpose. He or she also has to grant write permissions to the user (see below). +The handle server admin has to add the ``HS_PUBKEY`` entry with the user's public key to an existing handle or +create a new handle for this purpose. He or she also has to grant write permissions to the user +(see :ref:`givingpermissiontousers`.). -For creating the certificate, the following steps may be required: +For creating the certificate, please follow these instructions: :ref:`creatingclientcertificate`. -Creating the client certificate -------------------------------- - -1. Creating a private/public key pair: - - * For this, you can use the command line tool "hdl-keygen" that is shipped together with the handle system software: - - .. code:: json - - bash /.../handlesystem_software/hsj-8.x.x/bin/hdl-keygen - -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! - - * When it asks whether you want to encrypt the key, type 'n': - - .. code:: json - - Would you like to encrypt your private key? (y/n) [y] n - - Why? The b2handle library uses the python library *requests* which does not support encrypted private keys: - *"The private key to your local certificate must be unencrypted. Currently, requests does not support - using encrypted keys."* (see `requests documentation on this topic `__). - -2. Upload the user's public key to the ``HS_PUBKEY`` entry: - - * For this, you can use the command line tool "hdl-admintool" that is shipped together with the handle system software: - - .. code:: json - - bash /.../handlesystem_software/hsj-8.x.x/bin/hdl-admintool - - * Authenticate with your handle-server-admin credentials - * Lookup the handle where you want to store the user's public key (foo/bar) - * "Edit" - * "Add" > "Blank Value" - * Index: In this example, we use 301. As a convention, take the lowest value >= 301 that is not in use yet. - * "Load from file" > Choose the file "301_foo_bar_pubkey.bin" - * The value type should have set itself to *"Hex"* now. - * Don't forget to give admin permissions to the username 301:foo/bar, where you just uploaded the public key! - -3. Transforming the binary private key (.bin) to a .pem file: - - * For this, you can use the command line tool "hdl-convert-key" that is shipped together with the handle system software: - - .. code:: json - - bash /.../handlesystem_software/hsj-8.x.x/bin/hdl-convert-key - /.../301_foo_bar_privkey.bin - -o /.../301_foo_bar_privkey.pem - -4. Creating the certificate file: - - * 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 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: - - .. code-block:: none - :emphasize-lines: 13,14 - - You are about to be asked to enter information that will be incorporated - into your certificate request. - What you are about to enter is what is called a Distinguished Name or a DN. - There are quite a few fields but you can leave some blank - For some fields there will be a default value, - If you enter '.', the field will be left blank. - ----- - Country Name (2 letter code) [XX]: - State or Province Name (full name) []: - Locality Name (eg, city) [Default City]: - Organization Name (eg, company) [Default Company Ltd]: - Organizational Unit Name (eg, section) []: - Common Name (eg, your name or your server's hostname) []:300:foo/bar - Email Address []: - -5. Optional: Removing the public key from the certificate file: - - .. code:: json - - openssl x509 -inform PEM -in /.../301_certificate_and_publickey.pem - -out /.../301_certificate_only.pem - -Now, the certificate_only.pem file and the private_key.pem file can be used for authentication. -The paths to these files should be entered into the JSON credentials file asfollows:: - - { - "handle_server_url": "https://my.handle.server", - "private_key": "301_foo_bar_privkey.pem", - "certificate_only": "301_certificate_only.pem" - } - -Please follow the client documentation to see how a user can use this JSON file to authenticate while using the b2handle library. - - -Giving admin permissions to users -================================= - -No matter which of the methods is used, in both cases the Handle Server admin (or prefix owner) has to give write permissions to -the user. The admin can do that in several ways. Note that while the third method looks most complex, it may be the easiest one, -as it is most easily modified and extended (without having to contact the prefix provider to changes in the **0.NA/foo** record). - -These are three ways to grant admin permissions to users **300:foo/bar** and **301:foo/bar**: - -1. By creating a ``HS_ADMIN`` entry for each username in the prefix owner handle record (i.e. somewhere in the record **0.NA/foo**). - - **Handle record 0.NA/foo:** - - ===== ========= ======================= - Index Key Value - ... ... ... - 100 HS_ADMIN (refers to 300:foo/bar) - 101 HS_ADMIN (refers to 301:foo/bar) - ... ... ... - ===== ========= ======================= - - **Handle record foo/bar:** - - ===== ========= ========== - Index Key Value - ... ... ... - 300 HS_SECKEY *mypassword* - 301 HS_PUBKEY 0000A552100 - ... ... ... - ===== ========= ========== - -2. By adding the usernames (**300:foo/bar** and **301:foo/bar**) to a ``HS_VLIST`` entry in the prefix owner handle record - (i.e. somewhere in the record **0.NA/foo**), which was referenced in a ``HS_ADMIN`` entry in **0.NA/foo**. - - **Handle record 0.NA/foo:** - - ===== ========= ======================= - Index Key Value - ... ... ... - 100 HS_ADMIN (refers to 200:0.NA/foo) - 200 HS_VLIST 300:foo/bar - 301:foo/bar - ... ... ... - ===== ========= ======================= - - **Handle record foo/bar:** - - ===== ========= ========== - Index Key Value - ... ... ... - 300 HS_SECKEY *mypassword* - 301 HS_PUBKEY 0000A552100 - ... ... ... - ===== ========= ========== - -3. By adding the usernames (**300:foo/bar** and **301:foo/bar**) to any ``HS_VLIST`` entry referenced somewhere in **0.NA/foo**. - For example, if there is a ``HS_ADMIN`` at index 101 of **0.NA/foo** which points to a ``HS_VLIST`` at the index 200 in - **0.NA/foo**, which points to a ``HS_VLIST`` at index 200 in 'foo/admin', which points to a ``HS_SECKEY`` at index 300 in 'foo/bar' - then **300:foo/bar** is a username with all the permissions stated in the ``HS_ADMIN`` entry at the index 101 of **0.NA/foo**. - - **Handle record 0.NA/foo:** - - ===== ========= ======================= - Index Key Value - ... ... ... - 100 HS_ADMIN (refers to 200:0.NA/foo) - 200 HS_VLIST 200:foo/bar - ... ... ... - ===== ========= ======================= - - **Handle record foo/bar:** - - ===== ========= ======================= - Index Key Value - ... ... ... - 200 HS_VLIST 300:foo/bar - 301:foo/bar - 300 HS_SECKEY *mypassword* - 301 HS_PUBKEY 0000A552100 - ... ... ... - ===== ========= ======================= Common problems =============== @@ -249,6 +57,22 @@ Some common problems when authenticating, together with possible solutions. Plea causes are causes we observed. Of course it is possible that other reasons may cause the same problems, in that case these solutions may not work. +HTTP 401 +-------- + + **Problem:** + + * The handle server returns a JSON object that looks like this: ``{"responseCode":402,"handle":"myprefix/123456"}`` + * Handle Server responseCode 402 (*Authentication needed*) + * HTTP status code 401 (*Unauthorized*) + + **Possible Solution:** + + This error occurs if the client certificate was not correctly passed to the handle server. Possibly the server + forwards the request internally to a different port and loses the certificate information on the way (e.g. using httpd ProxyPass). + Please ask your handle server administrator about this. Testing the same request directly on the port of the handle server (if + that is open for external access) can help finding out whether this is the problem. + HTTP 403 -------- @@ -277,36 +101,40 @@ Handshake Failure ``SSL routines:SSL3_READ_BYTES:ssl handshake failure`` - **Possible Solution:** - Sometimes, this error occurs if the private key was encrypted. Please try with an unencrypted private key. - This can occur with unencrypted keys, too. We have no proposed solution for that. + **Possible Solution 1:** -HTTP 401 --------- + This error can occur if the private key was encrypted. Please try with an unencrypted private key. + + **Possible Solution 2:** + + Make sure that openssl version 1.0.1 or higher is used. Openssl 0.98 gives handshake errors. + +SSL Error +--------- **Problem:** - * HTTP status code 401 (*Unauthorized*) - * HS response code 402 (*Authentication needed*) - * The handle server returns a JSON object that looks like this: ``{"responseCode":402,"handle":"myprefix/123456"}`` + ``requests.exceptions.SSLError: [SSL] PEM lib (_ssl.c:2525)`` **Possible Solution:** - This error occurs if the client certificate was not correctly passed to the handle server. Possibly the server - forwards the request internally to a different port and loses the certificate information on the way (e.g. using httpd ProxyPass). - Please ask your handle server administrator about this. Testing the same request directly on the port of the handle server (if - that is open for external access) can help finding out whether this is the problem. + This error occurs if the private key was not provided, for example if a single file instead of two was provided, + but the private key was not contained. For this reason, we only recommend and describe passing certificate and + private key in two separate files. SSL Error --------- **Problem:** - ``requests.exceptions.SSLError: [SSL] PEM lib (_ssl.c:2525)`` + ``SSLError: SSL3_GET_SERVER_CERTIFICATE:certificate verify failed`` **Possible Solution:** - This error occurs if the private key was not provided, for example if a single file instead of two was provided, - but the private key was not contained. FOr this reason, we only recommend and describe passing certificate and - private key in two separate files. + This error occurs if the server certificate at the handle server can not be verified at the client side. The library + default is to verify the certificate. This is normally done with a certificate from a CA authority. The credentials + file can have an optional parameter ``HTTPS_verify`` to change the behaviour. The problem can be solved in several ways. + By adding the correct CA certificate to the bundle on the system. By setting a path to the correct CA certificate as follows: + ``"HTTPS_verify": "/path_to_ca_certificate/ca_certificate"``. Or by disabling the checking of the certificate: + ``"HTTPS_verify": "False"``. The last option is the least desired option. diff --git a/docs/source/creatingclientcertificates.rst b/docs/source/creatingclientcertificates.rst new file mode 100644 index 0000000..4b803ed --- /dev/null +++ b/docs/source/creatingclientcertificates.rst @@ -0,0 +1,146 @@ +.. _creatingclientcertificate: + +=============================== +Creating the client certificate +=============================== + +For authentication using client certificates, a special pair of keys and a certificate file is required. +Follow these five steps to create them for your users: + +1. :ref:`step1`. +2. :ref:`step2`. +3. :ref:`step3`. +4. :ref:`step4`. +5. :ref:`step5`. + + +.. _step1: + +Creating a private/public key pair +================================== + + * For this, you can use the command line tool "hdl-keygen" that is shipped together with the handle system software: + + .. code:: json + + bash /.../handlesystem_software/hsj-8.x.x/bin/hdl-keygen + -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! + + * When it asks whether you want to encrypt the key, type 'n': + + .. code:: json + + Would you like to encrypt your private key? (y/n) [y] n + + Why? The b2handle library uses the python library *requests* which does not support encrypted private keys: + *"The private key to your local certificate must be unencrypted. Currently, requests does not support + using encrypted keys."* (see `requests documentation on this topic `__). + + +.. _step2: + +Upload the user's public key to the ``HS_PUBKEY`` entry +======================================================= + + * For this, you can use the command line tool "hdl-admintool" that is shipped together with the handle system software: + + .. code:: json + + bash /.../handlesystem_software/hsj-8.x.x/bin/hdl-admintool + + * Authenticate with your handle-server-admin credentials + * Lookup the handle where you want to store the user's public key (foo/bar) + * "Edit" + * "Add" > "Blank Value" + * Index: In this example, we use 301. As a convention, take the lowest value >= 301 that is not in use yet. + * "Load from file" > Choose the file "301_foo_bar_pubkey.bin" + * The value type should have set itself to *"Hex"* now. + * Don't forget to give admin permissions to the username 301:foo/bar, where you just uploaded the public key! + + +.. _step3: + +Transforming the binary private key (.bin) to a .pem file +========================================================= + + * For this, you can use the command line tool "hdl-convert-key" that is shipped together with the handle system software: + + .. code:: json + + bash /.../handlesystem_software/hsj-8.x.x/bin/hdl-convert-key + /.../301_foo_bar_privkey.bin + -o /.../301_foo_bar_privkey.pem + +.. _step4: + +Creating the certificate file +============================= + + * 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 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: + + .. code-block:: none + :emphasize-lines: 13,14 + + You are about to be asked to enter information that will be incorporated + into your certificate request. + What you are about to enter is what is called a Distinguished Name or a DN. + There are quite a few fields but you can leave some blank + For some fields there will be a default value, + If you enter '.', the field will be left blank. + ----- + Country Name (2 letter code) [XX]: + State or Province Name (full name) []: + Locality Name (eg, city) [Default City]: + Organization Name (eg, company) [Default Company Ltd]: + Organizational Unit Name (eg, section) []: + Common Name (eg, your name or your server's hostname) []:300:foo/bar + Email Address []: + + +.. _step5: + +Removing the public key from the certificate file +================================================= + + .. code:: json + + openssl x509 -inform PEM -in /.../301_certificate_and_publickey.pem + -out /.../301_certificate_only.pem + +Usage +===== + +Now, the certificate_only.pem file and the private_key.pem file can be used for authentication. +The paths to these files should be entered into the JSON credentials file asfollows:: + + { + "handle_server_url": "https://my.handle.server", + "private_key": "301_foo_bar_privkey.pem", + "certificate_only": "301_certificate_only.pem" + } + +Please follow the client documentation to see how a user can use this JSON file to authenticate while using the b2handle library. \ No newline at end of file diff --git a/docs/source/givingpermissiontousers.rst b/docs/source/givingpermissiontousers.rst new file mode 100644 index 0000000..a17b9ee --- /dev/null +++ b/docs/source/givingpermissiontousers.rst @@ -0,0 +1,228 @@ +.. _givingpermissiontousers: + +================================= +Giving write permissions to users +================================= + +No matter which of the methods for *authentication* is used, in both cases the +Handle Server admin (or prefix owner) has to give write permissions to +the user (*authorisation*): + +* Authentication: Are you who you claim to be? +* Authorisation: Are you allowed to do what you are trying to do? + +These are several ways to grant write permissions to users **300:foo/bar** +and **301:foo/bar** and **300:foo/doe**: + +1. :ref:`method1`. +2. :ref:`method2`. +3. :ref:`method3`. + +Please note that while the third method looks most complex, it may be +the easiest one, as it is most easily modified and extended (without +having to contact the prefix provider to make changes in the **0.NA/foo** record). + +.. _method1: + +HS_ADMIN entry for each username in the prefix owner handle record +================================================================== + +We can give users write permissions by creating a ``HS_ADMIN`` entry +for each username in the prefix owner handle record (i.e. somewhere +in the record **0.NA/foo**). + + + **Handle record 0.NA/foo:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 100 HS_ADMIN (refers to 300:foo/bar) + 101 HS_ADMIN (refers to 301:foo/bar) + 102 HS_ADMIN (refers to 300:foo/doe) + ... ... ... + ===== ========= ======================= + + **Handle record foo/bar:** + + ===== ========= ========== + Index Key Value + ... ... ... + 300 HS_SECKEY *mypassword* + 301 HS_PUBKEY 0000A552100 + ... ... ... + ===== ========= ========== + + **Handle record foo/doe:** + + ===== ========= ========== + Index Key Value + ... ... ... + 300 HS_SECKEY *mypassword* + ... ... ... + ===== ========= ========== + + + +.. _method2: + +HS_VLIST entry containing usernames in the prefix owner handle record +===================================================================== + +We can grant users write permissions by adding the usernames (**300:foo/bar**, +**301:foo/bar** and **300:foo/doe**) to a ``HS_VLIST`` entry in the +prefix owner handle record (i.e. somewhere in the record **0.NA/foo**), +which was referenced in a ``HS_ADMIN`` entry in **0.NA/foo**. + + **Handle record 0.NA/foo:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 100 HS_ADMIN (refers to 200:0.NA/foo) + 200 HS_VLIST 300:foo/bar + 301:foo/bar + 300:foo/doe + ... ... ... + ===== ========= ======================= + + **Handle record foo/bar:** + + ===== ========= ========== + Index Key Value + ... ... ... + 300 HS_SECKEY *mypassword* + 301 HS_PUBKEY 0000A552100 + ... ... ... + ===== ========= ========== + + **Handle record foo/doe:** + + ===== ========= ========== + Index Key Value + ... ... ... + 300 HS_SECKEY *mypassword* + ... ... ... + ===== ========= ========== + + +.. _method3: + +HS_VLIST entry containing usernames in another place +==================================================== + +We can give users write permissions by adding the usernames (**300:foo/bar**, **301:foo/bar** +and **300:foo/doe**) to any ``HS_VLIST`` entry referenced somewhere in **0.NA/foo**. + + +The difference to the previous method is: This ``HS_VLIST`` does not have to be inside +the **0.NA/foo** record, it only has to be referenced there - it can be put into +a different handle, e.g. **foo/admin**, so changes to the ``HS_VLIST`` can be made +without having to ask the prefix provider (who is usually the only one able to change +entries in **0.NA/foo**). + +For example, if there is a ``HS_ADMIN`` at index 101 of **0.NA/foo** which points to +a ``HS_VLIST`` at the index 200 in **0.NA/foo**, which points to a ``HS_VLIST`` at +index 200 in 'foo/admin', which points to a ``HS_SECKEY`` at index 300 in 'foo/bar' - +then **300:foo/bar** is a username with all the permissions stated in the ``HS_ADMIN`` +entry at the index 101 of **0.NA/foo**. + + + **Handle record 0.NA/foo:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 100 HS_ADMIN (refers to 200:0.NA/foo) + 200 HS_VLIST 200:foo/admin + ... ... ... + ===== ========= ======================= + + **Handle record foo/admin:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 200 HS_VLIST 300:foo/bar + 301:foo/bar + 300:foo/doe + ... ... ... + ===== ========= ======================= + + **Handle record foo/bar:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 300 HS_SECKEY *mypassword* + 301 HS_PUBKEY 0000A552100 + ... ... ... + ===== ========= ======================= + + **Handle record foo/doe:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 300 HS_SECKEY *mypassword* + ... ... ... + ===== ========= ======================= + + .. important:: This setting gives write permissions to users foo/bar and foo/doe. + You should also make sure that those users are not able to change other people's + write permissions. For this, make sure the ``HS_ADMIN`` entries of the handles concerned + with user administration point to a username or ``HS_VLIST`` that only you can + access. + +To ensure that only you (or your admin colleagues) can change users' write permissions, +we add a list of admins (another ``HS_VLIST``) to the admin handle record (foo/admin) +and reference it in the ``HS_ADMIN`` entry of the admin handle record. Only the users +in this list can administer users. + + **Handle record 0.NA/foo:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 100 HS_ADMIN (refers to 200:0.NA/foo) + 200 HS_VLIST 200:foo/admin + ... ... ... + ===== ========= ======================= + + **Handle record foo/admin:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 100 HS_ADMIN (refers to 201:foo/admin) + 200 HS_VLIST 300:foo/bar + 301:foo/bar + 300:foo/doe + 201 HS_VLIST 300:foo/admin + 301:foo/admin + 300 HS_SECKEY *myadminpassword* + 301 HS_PUBKEY 0000B652300 + ... ... ... + ===== ========= ======================= + + **Handle record foo/bar:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 100 HS_ADMIN (refers to 201:foo/admin) + 300 HS_SECKEY *mypassword* + 301 HS_PUBKEY 0000A552100 + ... ... ... + ===== ========= ======================= + + **Handle record foo/doe:** + + ===== ========= ======================= + Index Key Value + ... ... ... + 100 HS_ADMIN (refers to 201:foo/admin) + 300 HS_SECKEY *mypassword* + ... ... ... + ===== ========= ======================= + diff --git a/docs/source/index.rst b/docs/source/index.rst index 8db53ab..bbe5193 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,6 +15,8 @@ Contents: handleclient authentication + creatingclientcertificates + givingpermissiontousers handleserverconfig diff --git a/setup.py b/setup.py index 73d770f..c0403aa 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages import sys, os -version = '1.0.1' +version = '1.0.2' # Set common test dependencies test_dependencies = [