Repository: EdinburghGenomics/EGCG-Core

Issue:
Authentication
Since EdinburghGenomics/Reporting-App will be adding authentication, we should add auth headers to `rest_communication`.

Discussion:

Patch:
diff --git a/egcg_core/rest_communication.py b/egcg_core/rest_communication.py
index 942039b..39acd98 100644
--- a/egcg_core/rest_communication.py
+++ b/egcg_core/rest_communication.py
@@ -1,9 +1,10 @@
 import requests
 from urllib.parse import urljoin
-from egcg_core.config import default as cfg
+from egcg_core.config import default
 from egcg_core.app_logging import logging_default as log_cfg
 from egcg_core.exceptions import RestCommunicationError
 
+cfg = default['rest_api']
 app_logger = log_cfg.get_logger(__name__)
 
 table = {' ': '', '\'': '"', 'None': 'null'}
@@ -17,7 +18,7 @@ def _translate(s):
 
 def api_url(endpoint, **query_args):
     url = '{base_url}/{endpoint}/'.format(
-        base_url=cfg.query('rest_api', 'url').rstrip('/'), endpoint=endpoint
+        base_url=cfg['url'].rstrip('/'), endpoint=endpoint
     )
     if query_args:
         query = '?' + '&'.join(['%s=%s' % (k, v) for k, v in query_args.items()])
@@ -38,7 +39,11 @@ def _parse_query_string(query_string, requires=None):
 
 
 def _req(method, url, quiet=False, **kwargs):
-    r = requests.request(method, url, **kwargs)
+    auth = None
+    if 'username' in cfg and 'password' in cfg:
+        auth = (cfg['username'], cfg['password'])
+
+    r = requests.request(method, url, auth=auth, **kwargs)
     # e.g: 'POST <url> ({"some": "args"}) -> {"some": "content"}. Status code 201. Reason: CREATED
     report = '%s %s (%s) -> %s. Status code %s. Reason: %s' % (
         r.request.method, r.request.path_url, kwargs, r.content.decode('utf-8'), r.status_code, r.reason
@@ -46,6 +51,8 @@ def _req(method, url, quiet=False, **kwargs):
     if r.status_code in (200, 201):
         if not quiet:
             app_logger.debug(report)
+    elif r.status_code == 401:
+        raise RestCommunicationError('Invalid auth credentials')
     else:
         app_logger.error(report)
     return r
@@ -58,7 +65,6 @@ def get_content(endpoint, paginate=True, quiet=False, **query_args):
             page=query_args.pop('page', 1)
         )
     url = api_url(endpoint, **query_args)
-
     return _req('GET', url, quiet=quiet).json()
 
 
@@ -170,7 +176,7 @@ def post_or_patch(endpoint, input_json, id_field=None, update_lists=None):
         doc = get_document(endpoint, where={id_field: _payload[id_field]})
         if doc:
             _payload.pop(id_field)
-            s = _patch_entry(endpoint, doc, _payload, update_lists)
+            s = _patch_entry(endpoint, doc, _payload,  update_lists)
         else:
             s = post_entry(endpoint, _payload)
         success = success and s
diff --git a/etc/example_egcg.yaml b/etc/example_egcg.yaml
index 11b9ade..c6d764e 100644
--- a/etc/example_egcg.yaml
+++ b/etc/example_egcg.yaml
@@ -4,6 +4,8 @@ default:
 
     rest_api:
         url: 'http://localhost:4999/api/0.1'
+        username: 'a_user'
+        password: 'a_password'
 
     ncbi_cache: ':memory:'
 


Test patch:
diff --git a/tests/test_rest_communication.py b/tests/test_rest_communication.py
index c43d6b7..b9905c2 100644
--- a/tests/test_rest_communication.py
+++ b/tests/test_rest_communication.py
@@ -21,6 +21,7 @@ patched_response = patch(
     'requests.request',
     return_value=FakeRestResponse(status_code=200, content=test_request_content)
 )
+auth = ('a_user', 'a_password')
 
 
 def query_args_from_url(url):
@@ -74,7 +75,7 @@ def test_req(mocked_response):
     response = rest_communication._req('METHOD', rest_url(test_endpoint), json=json_content)
     assert response.status_code == 200
     assert json.loads(response.content.decode('utf-8')) == response.json() == test_request_content
-    mocked_response.assert_called_with('METHOD', rest_url(test_endpoint), json=json_content)
+    mocked_response.assert_called_with('METHOD', rest_url(test_endpoint), auth=auth, json=json_content)
 
 
 def test_get_documents_depaginate():
@@ -122,13 +123,13 @@ def test_get_document():
 @patched_response
 def test_post_entry(mocked_response):
     rest_communication.post_entry(test_endpoint, payload=test_request_content)
-    mocked_response.assert_called_with('POST', rest_url(test_endpoint), json=test_request_content)
+    mocked_response.assert_called_with('POST', rest_url(test_endpoint), auth=auth, json=test_request_content)
 
 
 @patched_response
 def test_put_entry(mocked_response):
     rest_communication.put_entry(test_endpoint, 'an_element_id', payload=test_request_content)
-    mocked_response.assert_called_with('PUT', rest_url(test_endpoint) + 'an_element_id', json=test_request_content)
+    mocked_response.assert_called_with('PUT', rest_url(test_endpoint) + 'an_element_id', auth=auth, json=test_request_content)
 
 
 test_patch_document = {
@@ -138,7 +139,7 @@ test_patch_document = {
 
 @patch('egcg_core.rest_communication.get_document', return_value=test_patch_document)
 @patched_response
-def test_patch_entry(mocked_request, mocked_get_doc):
+def test_patch_entry(mocked_response, mocked_get_doc):
     patching_payload = {'list_to_update': ['another']}
     rest_communication.patch_entry(
         test_endpoint,
@@ -149,10 +150,11 @@ def test_patch_entry(mocked_request, mocked_get_doc):
     )
 
     mocked_get_doc.assert_called_with(test_endpoint, where={'uid': 'a_unique_id'})
-    mocked_request.assert_called_with(
+    mocked_response.assert_called_with(
         'PATCH',
         rest_url(test_endpoint) + '1337',
         headers={'If-Match': 1234567},
+        auth=auth,
         json={'list_to_update': ['this', 'that', 'other', 'another']}
     )