Fix the logging of request body credentials

Recently we moved the logging of request from rest_client's
request() to raw_request which end up logging the credentials
also from Token client request.

This is to add a kwargs in raw_request() method which tell whether
request body needs to log or not. Also update Token clients to
pass '<omitted>' to stop logging the credential.

Change-Id: I7cbd3656463a51d18b4d72f45a324145283fc7eb
diff --git a/releasenotes/notes/fix-credential-logging-98089c897d801355.yaml b/releasenotes/notes/fix-credential-logging-98089c897d801355.yaml
new file mode 100644
index 0000000..e06e2e2
--- /dev/null
+++ b/releasenotes/notes/fix-credential-logging-98089c897d801355.yaml
@@ -0,0 +1,6 @@
+---
+security:
+  - |
+    Recently we moved the logging of API request in rest_client's
+    raw_request which end up logging the credentials also from
+    Token client request. This issue has been fixed now.
diff --git a/tempest/lib/common/rest_client.py b/tempest/lib/common/rest_client.py
index 6cc0561..431a0a0 100644
--- a/tempest/lib/common/rest_client.py
+++ b/tempest/lib/common/rest_client.py
@@ -556,7 +556,8 @@
 
         return resp, resp_body
 
-    def raw_request(self, url, method, headers=None, body=None, chunked=False):
+    def raw_request(self, url, method, headers=None, body=None, chunked=False,
+                    log_req_body=None):
         """Send a raw HTTP request without the keystone catalog or auth
 
         This method sends a HTTP request in the same manner as the request()
@@ -572,6 +573,11 @@
                              explicitly requires no headers use an empty dict.
         :param str body: Body to send with the request
         :param bool chunked: sends the body with chunked encoding
+        :param str log_req_body: Whether to log the request body or not.
+                                 It is default to None which means request
+                                 body is safe to log otherwise pass any string
+                                 you want to log in place of request body.
+                                 For example: '<omitted>'
         :rtype: tuple
         :return: a tuple with the first entry containing the response headers
                  and the second the response body
@@ -585,8 +591,9 @@
             url, method, headers=headers,
             body=body, chunked=chunked)
         end = time.time()
+        req_body = body if log_req_body is None else log_req_body
         self._log_request(method, url, resp, secs=(end - start),
-                          req_headers=headers, req_body=body,
+                          req_headers=headers, req_body=req_body,
                           resp_body=resp_body)
         return resp, resp_body
 
diff --git a/tempest/lib/services/identity/v2/token_client.py b/tempest/lib/services/identity/v2/token_client.py
index 458c862..9f10f58 100644
--- a/tempest/lib/services/identity/v2/token_client.py
+++ b/tempest/lib/services/identity/v2/token_client.py
@@ -105,9 +105,8 @@
                 headers = self.get_headers(accept_type="json")
 
         resp, resp_body = self.raw_request(url, method,
-                                           headers=headers, body=body)
-        self._log_request(method, url, resp, req_headers=headers,
-                          req_body='<omitted>', resp_body=resp_body)
+                                           headers=headers, body=body,
+                                           log_req_body='<omitted>')
 
         if resp.status in [401, 403]:
             resp_body = json.loads(resp_body)
diff --git a/tempest/lib/services/identity/v3/token_client.py b/tempest/lib/services/identity/v3/token_client.py
index d591f03..6956297 100644
--- a/tempest/lib/services/identity/v3/token_client.py
+++ b/tempest/lib/services/identity/v3/token_client.py
@@ -160,10 +160,8 @@
                 headers = self.get_headers(accept_type="json")
 
         resp, resp_body = self.raw_request(url, method,
-                                           headers=headers, body=body)
-        self._log_request(method, url, resp, req_headers=headers,
-                          req_body='<omitted>', resp_body=resp_body)
-
+                                           headers=headers, body=body,
+                                           log_req_body='<omitted>')
         if resp.status in [401, 403]:
             resp_body = json.loads(resp_body)
             raise exceptions.Unauthorized(resp_body['error']['message'])
diff --git a/tempest/tests/lib/fake_identity.py b/tempest/tests/lib/fake_identity.py
index 8bae34f..9d7b0fd 100644
--- a/tempest/tests/lib/fake_identity.py
+++ b/tempest/tests/lib/fake_identity.py
@@ -192,7 +192,7 @@
 
 
 def _fake_v3_response(self, uri, method="GET", body=None, headers=None,
-                      redirections=5, connection_type=None):
+                      redirections=5, connection_type=None, log_req_body=None):
     fake_headers = {
         "x-subject-token": TOKEN
     }
@@ -202,7 +202,7 @@
 
 def _fake_v3_response_domain_scope(self, uri, method="GET", body=None,
                                    headers=None, redirections=5,
-                                   connection_type=None):
+                                   connection_type=None, log_req_body=None):
     fake_headers = {
         "status": "201",
         "x-subject-token": TOKEN
@@ -213,7 +213,7 @@
 
 def _fake_v3_response_no_scope(self, uri, method="GET", body=None,
                                headers=None, redirections=5,
-                               connection_type=None):
+                               connection_type=None, log_req_body=None):
     fake_headers = {
         "status": "201",
         "x-subject-token": TOKEN
@@ -223,7 +223,7 @@
 
 
 def _fake_v2_response(self, uri, method="GET", body=None, headers=None,
-                      redirections=5, connection_type=None):
+                      redirections=5, connection_type=None, log_req_body=None):
     return (fake_http.fake_http_response({}, status=200),
             json.dumps(IDENTITY_V2_RESPONSE))
 
diff --git a/tempest/tests/lib/services/identity/v2/test_token_client.py b/tempest/tests/lib/services/identity/v2/test_token_client.py
index a592ada..5b4e210 100644
--- a/tempest/tests/lib/services/identity/v2/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v2/test_token_client.py
@@ -86,6 +86,9 @@
         with mock.patch.object(token_client_v2, 'raw_request') as mock_raw_r:
             mock_raw_r.return_value = response, body
             resp, body = token_client_v2.request('GET', 'fake_uri')
+        mock_raw_r.assert_called_once_with('fake_uri', 'GET',
+                                           headers=mock.ANY, body=None,
+                                           log_req_body='<omitted>')
         self.assertIsInstance(body, dict)
 
     def test_request_with_bytes_body(self):
diff --git a/tempest/tests/lib/services/identity/v3/test_token_client.py b/tempest/tests/lib/services/identity/v3/test_token_client.py
index a9c58df..656e10a 100644
--- a/tempest/tests/lib/services/identity/v3/test_token_client.py
+++ b/tempest/tests/lib/services/identity/v3/test_token_client.py
@@ -136,6 +136,9 @@
             mock_raw_r.return_value = (
                 fake_identity._fake_v3_response(None, None))
             resp, body = token_client_v3.request('GET', 'fake_uri')
+        mock_raw_r.assert_called_once_with('fake_uri', 'GET',
+                                           headers=mock.ANY, body=None,
+                                           log_req_body='<omitted>')
 
         self.assertIsInstance(body, dict)