Feature: Add authentication as app -- Breaking Change!

Previously 'app' auth was actually authentication as an 'installation'.
This corrects that. Use auth="installation".
This commit is contained in:
Kyle Hornberg
2018-02-22 15:45:34 -06:00
parent 46e184a022
commit f1e93c792b
4 changed files with 54 additions and 17 deletions
+4
View File
@@ -129,6 +129,10 @@ app::
octokit = Octokit(auth='app', app_id=42, private_key=private_key)
app::
octokit = Octokit(auth='installation', app_id=42, private_key=private_key)
For applications provide the application id either from the ping webhook or the application's page on GitHub.
The :code:`private_key` is a string of your private key provided for the application.
The :code:`app` scheme will use the application id and private key to get a token for the first installation id of the application.
+1 -1
View File
@@ -21,7 +21,7 @@ def read(*names, **kwargs):
setup(
name='octokitpy',
version='0.5.0',
version='0.6.0',
license='MIT license',
description='Python client for GitHub API',
long_description='%s\n%s' % (
+16 -4
View File
@@ -55,6 +55,7 @@ class Base(object):
authentication_schemes = {
'basic': self._setup_basic_authentication,
'token': self._setup_token_authentication,
'installation': self._setup_installation_authentication,
'app': self._setup_app_authentication,
}
if kwargs.get('auth'):
@@ -72,13 +73,20 @@ class Base(object):
self.token = kwargs['token']
self.auth = kwargs['auth']
def _setup_app_authentication(self, kwargs):
def _setup_installation_authentication(self, kwargs):
assert kwargs['app_id']
assert kwargs['private_key']
self.token, self.expires_at = self._app_auth_get_token(kwargs['app_id'], kwargs['private_key'])
self.auth = kwargs['auth']
self.headers['accept'] = 'application/vnd.github.machine-man-preview+json'
def _setup_app_authentication(self, kwargs):
assert kwargs['app_id']
assert kwargs['private_key']
self.jwt = self._app_auth_get_jwt(kwargs['app_id'], kwargs['private_key'])
self.auth = kwargs['auth']
self.headers['accept'] = 'application/vnd.github.machine-man-preview+json'
def _app_auth_get_token(self, app_id, key):
headers = {
'Authorization': 'Bearer {}'.format(self._app_auth_get_jwt(app_id, key)),
@@ -86,8 +94,8 @@ class Base(object):
}
installation_url = '{}/app/installations'.format(self.base_url)
installations = requests.get(installation_url, headers=headers).json()
installation_id = installations[0]['id']
installation_token_url = '{}/installations/{}/access_tokens'.format(self.base_url, installation_id)
self.installation_id = [x.get('id') for x in installations if x.get('app_id') == app_id].pop()
installation_token_url = '{}/installations/{}/access_tokens'.format(self.base_url, self.installation_id)
response = requests.post(installation_token_url, headers=headers).json()
return response['token'], response['expires_at']
@@ -102,7 +110,11 @@ class Base(object):
def _auth(self, requests_kwargs):
if getattr(self, 'auth', None) == 'basic':
return {'auth': (self.username, self.password)}
if getattr(self, 'auth', None) in ['token', 'app']:
if getattr(self, 'auth', None) in ['app']:
headers = requests_kwargs['headers']
headers.update({'Authorization': 'Bearer {}'.format(self.jwt)})
return {'headers': headers}
if getattr(self, 'auth', None) in ['token', 'installation']:
headers = requests_kwargs['headers']
headers.update({'Authorization': 'token {}'.format(self.token)})
return {'headers': headers}
+33 -12
View File
@@ -58,38 +58,39 @@ class TestAuth(object):
headers=headers,
)
def test_can_set_app_authentication(self, mocker):
def test_can_set_installation_authentication(self, mocker):
Request = namedtuple('Request', ['json'])
get = mocker.patch('requests.get')
get.return_value = Request(json=lambda: [{'id': 37}])
get.return_value = Request(json=lambda: [{'id': 13, 'app_id': 1}, {'id': 37, 'app_id': 42}])
post = mocker.patch('requests.post')
post.return_value = Request(json=lambda: {'token': 'v1.1f699f1069f60', 'expires_at': '2016-07-11T22:14:10Z'})
with open(os.path.join(os.path.dirname(__file__), 'test.pem'), 'r') as f:
private_key = f.read()
sut = Octokit(auth='app', app_id=42, private_key=private_key)
assert sut.auth == 'app'
sut = Octokit(auth='installation', app_id=42, private_key=private_key)
assert sut.auth == 'installation'
assert sut.token == 'v1.1f699f1069f60'
assert sut.expires_at == '2016-07-11T22:14:10Z'
def test_cannot_set_app_authentication_with_out_required_data(self):
def test_cannot_set_installation_authentication_with_out_required_data(self):
with pytest.raises(KeyError):
Octokit(auth='app')
Octokit(auth='installation')
with pytest.raises(AssertionError):
Octokit(auth='app', app_id='')
Octokit(auth='installation', app_id='')
with pytest.raises(KeyError):
Octokit(auth='app', app_id=42)
Octokit(auth='installation', app_id=42)
with pytest.raises(AssertionError):
Octokit(auth='app', app_id=42, private_key='')
Octokit(auth='installation', app_id=42, private_key='')
def test_app_token_is_used_if_set(self, mocker):
def test_installation_token_is_used_if_set(self, mocker):
Request = namedtuple('Request', ['json'])
get = mocker.patch('requests.get')
get.return_value = Request(json=lambda: [{'id': 37}])
get.return_value = Request(json=lambda: [{'id': 13, 'app_id': 1}, {'id': 37, 'app_id': 42}])
post = mocker.patch('requests.post')
post.return_value = Request(json=lambda: {'token': 'v1.1f699f1069f60', 'expires_at': '2016-07-11T22:14:10Z'})
with open(os.path.join(os.path.dirname(__file__), 'test.pem'), 'r') as f:
private_key = f.read()
sut = Octokit(auth='app', app_id=42, private_key=private_key)
sut = Octokit(auth='installation', app_id=42, private_key=private_key)
assert sut.installation_id == 37
get = mocker.patch('requests.get')
sut.authorization.get(id=100)
headers = {
@@ -102,3 +103,23 @@ class TestAuth(object):
params={},
headers=headers,
)
def test_cannot_set_app_authentication_with_out_required_data(self):
with pytest.raises(KeyError):
Octokit(auth='app')
with pytest.raises(AssertionError):
Octokit(auth='app', app_id='')
with pytest.raises(KeyError):
Octokit(auth='app', app_id=42)
with pytest.raises(AssertionError):
Octokit(auth='app', app_id=42, private_key='')
def test_can_set_app_authentication(self, mocker):
Request = namedtuple('Request', ['json'])
get = mocker.patch('requests.get')
get.return_value = Request(json=lambda: [{'id': 37}])
with open(os.path.join(os.path.dirname(__file__), 'test.pem'), 'r') as f:
private_key = f.read()
sut = Octokit(auth='app', app_id=42, private_key=private_key)
assert sut.auth == 'app'
assert sut.jwt is not None