From 6aaa330b99cbb132912f02f31d6a23d0f74ac14f Mon Sep 17 00:00:00 2001 From: Jahan Chaware Date: Thu, 8 Jun 2023 13:35:58 +0530 Subject: [PATCH 1/3] adding OAuth support to Client --- bitbucket/base.py | 20 +++++++++- bitbucket/client.py | 96 ++++++++++++++++++++++++++++++++------------- 2 files changed, 87 insertions(+), 29 deletions(-) diff --git a/bitbucket/base.py b/bitbucket/base.py index 6b5b782..6efc6ec 100644 --- a/bitbucket/base.py +++ b/bitbucket/base.py @@ -1,4 +1,5 @@ import typing +import requests from .exceptions import ( InvalidIDError, @@ -11,11 +12,28 @@ class BaseClient(object): BASE_URL = "https://api.bitbucket.org/" + TOKEN_URL = 'https://bitbucket.org/site/oauth2/access_token' - def __init__(self, user: str, password: str, owner: typing.Union[str, None] = None): + def __init__(self, user: str=None, password: str=None,token: str=None,client_id: str=None, client_secret: str=None, owner: typing.Union[str, None] = None): self.user = user self.password = password self.username = owner + self.use_password = False + self.use_token = False + if user and password: + self.use_password = True + self.token = token + if token: + self.use_token = True + elif client_id and client_secret: + token_req_payload = {'grant_type': 'client_credentials'} + response = requests.post(self.TOKEN_URL, data=token_req_payload, allow_redirects=False, auth=(client_id, client_secret)) + response = self._parse(response) + self.token = response['access_token'] + self.use_token = True + + if not (self.use_password or self.token): + raise NotAuthenticatedError("Insufficient credentials") def parse(self, response) -> typing.Union[typing.Dict[str, typing.Any], None]: """ diff --git a/bitbucket/client.py b/bitbucket/client.py index de1c430..6fe3be2 100644 --- a/bitbucket/client.py +++ b/bitbucket/client.py @@ -2,10 +2,10 @@ import requests from .base import BaseClient - +from .exceptions import NotAuthenticatedError class Client(BaseClient): - def __init__(self, user, password, owner=None): + def __init__(self, user=None, password=None, token=None, client_id=None, client_secret=None, owner=None): """Initial session with user/password, and setup repository owner Args: @@ -14,7 +14,7 @@ def __init__(self, user, password, owner=None): Returns: """ - super().__init__(user, password, owner) + super().__init__(user, password,token,client_id,client_secret, owner) # for shared repo, set baseURL to owner if owner is None: @@ -424,34 +424,74 @@ def delete_webhook(self, repository_slug, webhook_uid, params=None): params=params, ) - def _get(self, endpoint: str, params=None): - response = requests.get( - endpoint if endpoint.startswith("http") else self.BASE_URL + endpoint, - params=params, - auth=(self.user, self.password), - ) - return self.parse(response) + def _get(self, endpoint, params=None): + if self.use_password: + response = requests.get(self.BASE_URL + endpoint, params=params, auth=(self.user, self.password)) + elif self.use_token: + headers = { + "Accept": "application/json", + "Authorization": f"Bearer {self.token}" + } + response = requests.request( + "GET", + self.BASE_URL + endpoint, + params=params, + headers=headers + ) + else: + raise NotAuthenticatedError("Insufficient credentials") + return self._parse(response) def _post(self, endpoint, params=None, data=None): - response = requests.post( - self.BASE_URL + endpoint, - params=params, - json=data, - auth=(self.user, self.password), - ) - return self.parse(response) + if self.use_password: + response = requests.post(self.BASE_URL + endpoint, params=params, json=data, auth=(self.user, self.password)) + elif self.use_token: + headers = { + "Accept": "application/json", + "Authorization": f"Bearer {self.token}" + } + response = requests.request( + "POST", + self.BASE_URL + endpoint, + params=params, json=data, + headers=headers + ) + else: + raise NotAuthenticatedError("Insufficient credentials") + return self._parse(response) def _put(self, endpoint, params=None, data=None): - response = requests.put( - self.BASE_URL + endpoint, - params=params, - json=data, - auth=(self.user, self.password), - ) - return self.parse(response) + if self.use_password: + response = requests.put(self.BASE_URL + endpoint, params=params, json=data, auth=(self.user, self.password)) + elif self.use_token: + headers = { + "Accept": "application/json", + "Authorization": f"Bearer {self.token}" + } + response = requests.request( + "PUT", + self.BASE_URL + endpoint, + params=params, json=data, + headers=headers + ) + else: + raise NotAuthenticatedError("Insufficient credentials") + return self._parse(response) def _delete(self, endpoint, params=None): - response = requests.delete( - self.BASE_URL + endpoint, params=params, auth=(self.user, self.password) - ) - return self.parse(response) + if self.use_password: + response = requests.delete(self.BASE_URL + endpoint, params=params, auth=(self.user, self.password)) + elif self.use_token: + headers = { + "Accept": "application/json", + "Authorization": f"Bearer {self.token}" + } + response = requests.request( + "DELETE", + self.BASE_URL + endpoint, + params=params, + headers=headers + ) + else: + raise NotAuthenticatedError("Insufficient credentials") + return self._parse(response) From 2587f601f5206081cc38c39cd12b433fcd7d0973 Mon Sep 17 00:00:00 2001 From: Jahan Chaware Date: Thu, 8 Jun 2023 13:52:06 +0530 Subject: [PATCH 2/3] corrected _parse to parse --- bitbucket/base.py | 2 +- bitbucket/client.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bitbucket/base.py b/bitbucket/base.py index 6efc6ec..cbef966 100644 --- a/bitbucket/base.py +++ b/bitbucket/base.py @@ -28,7 +28,7 @@ def __init__(self, user: str=None, password: str=None,token: str=None,client_id: elif client_id and client_secret: token_req_payload = {'grant_type': 'client_credentials'} response = requests.post(self.TOKEN_URL, data=token_req_payload, allow_redirects=False, auth=(client_id, client_secret)) - response = self._parse(response) + response = self.parse(response) self.token = response['access_token'] self.use_token = True diff --git a/bitbucket/client.py b/bitbucket/client.py index 6fe3be2..4be1f46 100644 --- a/bitbucket/client.py +++ b/bitbucket/client.py @@ -440,7 +440,7 @@ def _get(self, endpoint, params=None): ) else: raise NotAuthenticatedError("Insufficient credentials") - return self._parse(response) + return self.parse(response) def _post(self, endpoint, params=None, data=None): if self.use_password: @@ -458,7 +458,7 @@ def _post(self, endpoint, params=None, data=None): ) else: raise NotAuthenticatedError("Insufficient credentials") - return self._parse(response) + return self.parse(response) def _put(self, endpoint, params=None, data=None): if self.use_password: @@ -476,7 +476,7 @@ def _put(self, endpoint, params=None, data=None): ) else: raise NotAuthenticatedError("Insufficient credentials") - return self._parse(response) + return self.parse(response) def _delete(self, endpoint, params=None): if self.use_password: @@ -494,4 +494,4 @@ def _delete(self, endpoint, params=None): ) else: raise NotAuthenticatedError("Insufficient credentials") - return self._parse(response) + return self.parse(response) From dd6162891a9b3b8428dc46d412c2dd052e0e1a98 Mon Sep 17 00:00:00 2001 From: Jahan Chaware Date: Wed, 5 Jul 2023 15:44:35 +0530 Subject: [PATCH 3/3] added OAuth and token support to async client] --- bitbucket/aclient.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/bitbucket/aclient.py b/bitbucket/aclient.py index 835a90d..cc8c5b8 100644 --- a/bitbucket/aclient.py +++ b/bitbucket/aclient.py @@ -23,12 +23,21 @@ class Client(BaseClient): """ async def __aenter__(self): - self._session = httpx.AsyncClient( - auth=( - self.user, - self.password, + if self.use_password: + self._session = httpx.AsyncClient( + auth=( + self.user, + self.password, + ) + ) + elif self.use_token: + headers = { + "Accept": "application/json", + "Authorization": f"Bearer {self.token}" + } + self._session = httpx.AsyncClient( + headers=headers ) - ) user_data = await self.get_user()