diff --git a/rtcclient/base.py b/rtcclient/base.py index 04ed1cf..aa41442 100644 --- a/rtcclient/base.py +++ b/rtcclient/base.py @@ -56,6 +56,7 @@ def get(self, url, verify=False, headers=None, + cookies=None, proxies=None, timeout=60, **kwargs): @@ -66,6 +67,8 @@ def get(self, A CA_BUNDLE path can also be provided. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with + the :class:`Request`. :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. :param timeout: (optional) How long to wait for the server to send data @@ -81,6 +84,7 @@ def get(self, response = requests.get(url, verify=verify, headers=headers, + cookies=cookies, proxies=proxies, timeout=timeout, **kwargs) @@ -97,6 +101,7 @@ def post(self, json=None, verify=False, headers=None, + cookies=None, proxies=None, timeout=60, **kwargs): @@ -111,6 +116,8 @@ def post(self, A CA_BUNDLE path can also be provided. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with + the :class:`Request`. :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. :param timeout: (optional) How long to wait for the server to send data @@ -129,6 +136,7 @@ def post(self, json=json, verify=verify, headers=headers, + cookies=cookies, proxies=proxies, timeout=timeout, **kwargs) @@ -146,6 +154,7 @@ def put(self, data=None, verify=False, headers=None, + cookies=None, proxies=None, timeout=60, **kwargs): @@ -158,6 +167,8 @@ def put(self, A CA_BUNDLE path can also be provided. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with + the :class:`Request`. :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. :param timeout: (optional) How long to wait for the server to send data @@ -174,6 +185,7 @@ def put(self, data=data, verify=verify, headers=headers, + cookies=cookies, proxies=proxies, timeout=timeout, **kwargs) @@ -187,6 +199,7 @@ def put(self, def delete(self, url, headers=None, + cookies=None, verify=False, proxies=None, timeout=60, @@ -196,6 +209,8 @@ def delete(self, :param url: URL for the new :class:`Request` object. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. + :param cookies: (optional) Dict or CookieJar object to send with + the :class:`Request`. :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided. :param proxies: (optional) Dictionary mapping protocol to the URL of @@ -212,6 +227,7 @@ def delete(self, self.log.debug("Delete a request to %s", url) response = requests.delete(url, headers=headers, + cookies=cookies, verify=verify, proxies=proxies, timeout=timeout, @@ -270,6 +286,7 @@ def _initialize(self): verify=False, proxies=self.rtc_obj.proxies, headers=self.rtc_obj.headers, + cookies=self.rtc_obj.cookies, ) self.__initialize(resp) self.log.info("Finish the initialization for <%s %s>", @@ -344,6 +361,7 @@ def __get_rdf_resource_title(self, rdf_url): verify=False, proxies=self.rtc_obj.proxies, headers=self.rtc_obj.headers, + cookies=self.rtc_obj.cookies, ) raw_data = xmltodict.parse(resp.content) diff --git a/rtcclient/client.py b/rtcclient/client.py index ac2ed38..507f243 100644 --- a/rtcclient/client.py +++ b/rtcclient/client.py @@ -71,6 +71,7 @@ def __init__(self, self.jazz = ends_with_jazz self.headers = self._get_headers() + self.cookies = self._get_cookies() self.searchpath = searchpath self.templater = Templater(self, searchpath=self.searchpath) self.query = Query(self) @@ -82,16 +83,20 @@ def get_rtc_obj(self): return self def _get_headers(self): + _headers = {} + _headers["Content-Type"] = self.CONTENT_XML + _headers["Accept"] = self.CONTENT_XML + return _headers + + def _get_cookies(self): if self.jazz is True: _allow_redirects = True else: _allow_redirects = False - _headers = {"Content-Type": self.CONTENT_XML} resp = self.get(self.url + "/authenticated/identity", auth=(self.username, self.password), verify=False, - headers=_headers, proxies=self.proxies, allow_redirects=_allow_redirects) @@ -107,23 +112,25 @@ def _get_headers(self): raise exception.RTCException("Authentication Failed: " "Invalid username or password") + _cookies = None + # fix issue #68 if not _allow_redirects: - if resp.headers.get("set-cookie") is not None: - _headers["Cookie"] = resp.headers.get("set-cookie") + _cookies = resp.cookies resp = self.get(self.url + "/authenticated/identity", auth=(self.username, self.password), verify=False, - headers=_headers, + cookies=_cookies, proxies=self.proxies, allow_redirects=_allow_redirects) # fix issue #68 if not _allow_redirects: - _headers["Cookie"] += "; " + resp.headers.get("set-cookie") + for cookie in resp.cookies: + _cookies.set_cookie(cookie) else: - _headers["Cookie"] = resp.headers.get("set-cookie") + _cookies = resp.cookies # For Form Challenge auth_msg = resp.headers.get("X-com-ibm-team-repository-web-auth-msg") @@ -137,6 +144,7 @@ def _get_headers(self): data=post_data, verify=False, headers=temp_headers, + cookies=_cookies, proxies=self.proxies, allow_redirects=True) @@ -157,12 +165,12 @@ def _get_headers(self): "Invalid username or password") if not _allow_redirects: - _headers["Cookie"] += "; " + resp.headers.get("set-cookie") + for cookie in resp.cookies: + _cookies.set_cookie(cookie) else: - _headers["Cookie"] = resp.headers.get("set-cookie") + _cookies = resp.cookies - _headers["Accept"] = self.CONTENT_XML - return _headers + return _cookies def relogin(self): """Relogin the RTC Server/Jazz when the token expires @@ -172,6 +180,7 @@ def relogin(self): self.log.info("Cookie expires. Relogin to get a new cookie.") self.headers = None self.headers = self._get_headers() + self.cookies = self._get_cookies() self.log.debug("Successfully relogin.") def getProjectAreas(self, archived=False, returned_properties=None): @@ -988,7 +997,8 @@ def getWorkitem(self, workitem_id, returned_properties=None): resp = self.get(req_url, verify=False, proxies=self.proxies, - headers=self.headers) + headers=self.headers, + cookies=self.cookies) raw_data = xmltodict.parse(resp.content) workitem_raw = raw_data["oslc_cm:ChangeRequest"] @@ -1213,6 +1223,7 @@ def _createWorkitem(self, url_post, workitem_raw): resp = self.post(url_post, verify=False, headers=headers, + cookies=self.cookies, proxies=self.proxies, data=workitem_raw) @@ -1456,7 +1467,8 @@ def _get_paged_resources(self, resp = self.get(resource_url, verify=False, proxies=self.proxies, - headers=self.headers) + headers=self.headers, + cookies=self.cookies) raw_data = xmltodict.parse(resp.content) try: @@ -1504,7 +1516,8 @@ def _get_paged_resources(self, resp = self.get(url_next, verify=False, proxies=self.proxies, - headers=self.headers) + headers=self.headers, + cookies=self.cookies) raw_data = xmltodict.parse(resp.content) else: break diff --git a/rtcclient/models.py b/rtcclient/models.py index 9de030c..7b1c14b 100644 --- a/rtcclient/models.py +++ b/rtcclient/models.py @@ -191,7 +191,8 @@ def getChanges(self): resp = self.get(resource_url, verify=False, proxies=self.rtc_obj.proxies, - headers=self.rtc_obj.headers) + headers=self.rtc_obj.headers, + cookies=self.rtc_obj.cookies) raw_data = xmltodict.parse(resp.content).get("scm:ChangeSet") common_changes = dict() changes = raw_data.get("changes") @@ -291,7 +292,10 @@ def _fetchFile(self, state_id, file_folder, override=True): self.log.debug("Start fetching file from %s ..." % file_url) - resp = self.get(file_url, verify=False, headers=self.rtc_obj.headers) + resp = self.get(file_url, + verify=False, + headers=self.rtc_obj.headers, + cookies=self.rtc_obj.cookies) file_name = re.findall(r".+filename\*=UTF-8''(.+)", resp.headers["content-disposition"])[0] file_path = os.path.join(file_folder, file_name) diff --git a/rtcclient/project_area.py b/rtcclient/project_area.py index d5be145..f5f7902 100644 --- a/rtcclient/project_area.py +++ b/rtcclient/project_area.py @@ -54,7 +54,8 @@ def getRoles(self): resp = self.get(roles_url, verify=False, proxies=self.rtc_obj.proxies, - headers=self.rtc_obj.headers) + headers=self.rtc_obj.headers, + cookies=self.rtc_obj.cookies) roles_list = list() raw_data = xmltodict.parse(resp.content) diff --git a/rtcclient/template.py b/rtcclient/template.py index 8e09ccf..c634cb9 100644 --- a/rtcclient/template.py +++ b/rtcclient/template.py @@ -278,7 +278,8 @@ def getTemplate(self, resp = self.get(workitem_url, verify=False, proxies=self.rtc_obj.proxies, - headers=self.rtc_obj.headers) + headers=self.rtc_obj.headers, + cookies=self.rtc_obj.cookies) raw_data = xmltodict.parse(resp.content) # pre-adjust the template: diff --git a/rtcclient/utils.py b/rtcclient/utils.py index 1b9ffb8..8ccd377 100644 --- a/rtcclient/utils.py +++ b/rtcclient/utils.py @@ -21,7 +21,7 @@ def token_expire_handler(func): def wrapper(*args, **kwargs): rtc_obj = args[0].get_rtc_obj() - if not hasattr(rtc_obj, "headers") or rtc_obj.headers is None: + if not hasattr(rtc_obj, "cookies") or rtc_obj.cookies is None: # still in the initialization or relogin # directly call the method return func(*args, **kwargs) @@ -39,7 +39,7 @@ def wrapper(*args, **kwargs): except RTCException: raise RTCException("Relogin Failed: " "Invalid username or password") - kwargs["headers"]["Cookie"] = rtc_obj.headers["Cookie"] + kwargs["cookies"] = rtc_obj.cookies return func(*args, **kwargs) else: # not expires diff --git a/rtcclient/workitem.py b/rtcclient/workitem.py index 236fe03..8e30aa6 100644 --- a/rtcclient/workitem.py +++ b/rtcclient/workitem.py @@ -105,7 +105,8 @@ def addComment(self, msg=None): resp = self.get(comments_url, verify=False, proxies=self.rtc_obj.proxies, - headers=headers) + headers=headers, + cookies=self.rtc_obj.cookies) raw_data = xmltodict.parse(resp.content) @@ -123,6 +124,7 @@ def addComment(self, msg=None): resp = self.post(req_url, verify=False, headers=headers, + cookies=self.rtc_obj.cookies, proxies=self.rtc_obj.proxies, data=comment_msg) self.log.info("Successfully add comment: [%s] for ", msg, @@ -236,6 +238,7 @@ def _update_subscribe(self, headers, raw_data): verify=False, proxies=self.rtc_obj.proxies, headers=headers, + cookies=self.rtc_obj.cookies, data=xmltodict.unparse(raw_data)) def _perform_subscribe(self): @@ -248,7 +251,8 @@ def _perform_subscribe(self): resp = self.get(subscribers_url, verify=False, proxies=self.rtc_obj.proxies, - headers=headers) + headers=headers, + cookies=self.rtc_obj.cookies) headers["If-Match"] = resp.headers.get("etag") raw_data = xmltodict.parse(resp.content) return headers, raw_data @@ -544,6 +548,7 @@ def addParent(self, parent_id): verify=False, proxies=self.rtc_obj.proxies, headers=headers, + cookies=self.rtc_obj.cookies, data=json.dumps(parent_original)) self.log.info( "Successfully add a parent to current " @@ -617,6 +622,7 @@ def _addChildren(self, child_ids): self.put(req_url, verify=False, headers=headers, + cookies=self.rtc_obj.cookies, proxies=self.rtc_obj.proxies, data=json.dumps(children_original)) @@ -670,6 +676,7 @@ def removeParent(self): verify=False, proxies=self.rtc_obj.proxies, headers=headers, + cookies=self.rtc_obj.cookies, data=json.dumps(parent_original)) self.log.info( "Successfully remove the parent workitem of current " @@ -738,6 +745,7 @@ def _removeChildren(self, child_ids): self.put(req_url, verify=False, headers=headers, + cookies=self.rtc_obj.cookies, proxies=self.rtc_obj.proxies, data=json.dumps(children_original)) @@ -771,6 +779,7 @@ def addAttachment(self, filepath): resp = self.post(req_url, verify=False, headers=headers, + cookies=self.rtc_obj.cookies, proxies=self.rtc_obj.proxies, params=params, files=files) @@ -794,6 +803,7 @@ def _add_attachment_link(self, attachment_info): payload, verify=False, headers=self.rtc_obj.headers, + cookies=self.rtc_obj.cookies, proxies=self.rtc_obj.proxies) raw_data = xmltodict.parse(resp.content) diff --git a/tests/conftest.py b/tests/conftest.py index da2980f..c763210 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,10 +8,10 @@ def rtcclient(mocker): mock_resp = mocker.MagicMock(spec=requests.Response) mock_resp.status_code = 200 - mock_resp.headers = {"set-cookie": "cookie-id"} + mock_resp.cookies = {"set-cookie": "cookie-id"} - mocked_headers = mocker.patch("rtcclient.client.RTCClient._get_headers") - mocked_headers.return_value = mock_resp + mocked_cookies = mocker.patch("rtcclient.client.RTCClient._get_cookies") + mocked_cookies.return_value = mock_resp return RTCClient(url="http://test.url:9443/jazz", username="tester1@email.com", diff --git a/tests/test_base.py b/tests/test_base.py index c8d34fe..54eb185 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -66,11 +66,13 @@ def test_get_resp(self, mocker): resp = test_rtc.get(test_rtc.url, verify=False, headers=test_rtc.CONTENT_XML, + cookies=None, proxies=None, timeout=30) mocked_get.assert_called_once_with(test_rtc.url, verify=False, headers=test_rtc.CONTENT_XML, + cookies=None, proxies=None, timeout=30) assert resp == mock_resp @@ -94,6 +96,7 @@ def test_post_resp(self, mocker): verify=False, proxies=None, headers=test_rtc.CONTENT_XML, + cookies=None, timeout=30) mocked_post.assert_called_once_with(test_rtc.url, data=post_data, @@ -101,6 +104,7 @@ def test_post_resp(self, mocker): verify=False, proxies=None, headers=test_rtc.CONTENT_XML, + cookies=None, timeout=30) assert resp == mock_resp @@ -123,6 +127,7 @@ def test_put_resp(self, mocker): verify=False, proxies=None, headers=test_rtc.CONTENT_XML, + cookies=None, timeout=30) mocked_put.assert_called_once_with(test_rtc.url, data=post_data, @@ -130,5 +135,6 @@ def test_put_resp(self, mocker): verify=False, proxies=None, headers=test_rtc.CONTENT_XML, + cookies=None, timeout=30) assert resp == mock_resp diff --git a/tests/test_client.py b/tests/test_client.py index ad6309f..e0f797f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -19,27 +19,23 @@ def test_headers(mocker): mocked_get.return_value = mock_rsp mocked_post.return_value = mock_rsp - expected_headers = { - "Content-Type": "text/xml", - "Cookie": "cookie-id", - "Accept": "text/xml" - } + expected_headers = {"Content-Type": "text/xml", "Accept": "text/xml"} + expected_cookies = {"set-cookie": "cookie-id"} # auth failed - mock_rsp.headers = { - "set-cookie": "cookie-id", - "x-com-ibm-team-repository-web-auth-msg": "authfailed" - } + mock_rsp.headers = {"x-com-ibm-team-repository-web-auth-msg": "authfailed"} with pytest.raises(RTCException): RTCClient(url="http://test.url:9443/jazz", username="user", password="password") - mock_rsp.headers = {"set-cookie": "cookie-id"} + mock_rsp.headers = {} + mock_rsp.cookies = {"set-cookie": "cookie-id"} client = RTCClient(url="http://test.url:9443/jazz", username="user", password="password") assert client.headers == expected_headers + assert client.cookies == expected_cookies class TestRTCClient: