8000 Provide TokenAuth class to requests to ensure explicit usage · sigmavirus24/github3.py@d829162 · GitHub
[go: up one dir, main page]

Skip to content

Commit d829162

Browse files
committed
Provide TokenAuth class to requests to ensure explicit usage
Provide a class based on BaseAuth that requests can all to set the required headers on any calls made to ensure that it only uses the given auth scheme. In the event that `auth` is set to None, requests will default to reading .netrc/_netrc files for any credentials set by the user. When given an auth token or password it's necessary to ensure that it will ignore any .netrc settings.
1 parent bf44c16 commit d829162

File tree

2 files changed

+59
-16
lines changed

2 files changed

+59
-16
lines changed

github3/session.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ def requires_2fa(response):
1717
return False
1818

1919

20+
class TokenAuth(requests.auth.AuthBase):
21+
def __init__(self, token):
22+
self.token = token
23+
24+
def __ne__(self, other):
25+
return not self == other
26+
27+
def __eq__(self, other):
28+
return self.token == getattr(other, 'token', None)
29+
30+
def __call__(self, request):
31+
request.headers['Authorization'] = 'token {}'.format(self.token)
32+
return request
33+
34+
2035
class GitHubSession(requests.Session):
2136
auth = None
2237
__attrs__ = requests.Session.__attrs__ + ['base_url', 'two_factor_auth_cb']
@@ -48,9 +63,6 @@ def basic_auth(self, username, password):
4863

4964
self.auth = (username, password)
5065

51-
# Disable token authentication
52-
self.headers.pop('Authorization', None)
53-
5466
def build_url(self, *args, **kwargs):
5567
"""Builds a new API url from scratch."""
5668
parts = [kwargs.get('base_url') or self.base_url]
@@ -122,11 +134,7 @@ def token_auth(self, token):
122134
if not token:
123135
return
124136

125-
self.headers.update({
126-
'Authorization': 'token {0}'.format(token)
127-
})
128-
# Unset username/password so we stop sending them
129-
self.auth = None
137+
self.auth = TokenAuth(token)
130138

131139
@contextmanager
132140
def temporary_basic_auth(self, *auth):

tests/unit/test_github_session.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,12 @@ def test_basic_login_disables_token_auth(self):
8585
"""
8686
s = self.build_session()
8787
s.token_auth('token goes here')
88-
assert 'Authorization' in s.headers
88+
req = requests.Request('GET', 'https://api.github.com/')
89+
pr = s.prepare_request(req)
90+
assert 'token token goes here' == pr.headers['Authorization']
8991
s.basic_auth('username', 'password')
90-
assert 'Authorization' not in s.headers
92+
pr = s.prepare_request(req)
93+
assert 'token token goes here' != pr.headers['Authorization']
9194

9295
@mock.patch.object(requests.Session, 'request')
9396
def test_handle_two_factor_auth(self, request_mock):
@@ -136,7 +139,9 @@ def test_token_auth(self):
136139
"""Test that token auth will work with a valid token"""
137140
s = self.build_session()
138141
s.token_auth('token goes here')
139-
assert s.headers['Authorization'] == 'token token goes here'
142+
req = requests.Request('GET', 'https://api.github.com/')
143+
pr = s.prepare_request(req)
144+
assert pr.headers['Authorization'] == 'token token goes here'
140145

141146
def test_token_auth_disables_basic_auth(self):
142147
"""Test that using token auth removes the value of the auth attribute.
@@ -146,15 +151,43 @@ def test_token_auth_disables_basic_auth(self):
146151
s = self.build_session()
147152
s.auth = ('foo', 'bar')
148153
s.token_auth('token goes here')
149-
assert s.auth is None
154+
assert s.auth != ('foo', 'bar')
150155

151156
def test_token_auth_does_not_use_falsey_values(self):
152157
"""Test that token auth will not authenticate with falsey values"""
153158
bad_tokens = [None, '']
159+
req = requests.Request('GET', 'https://api.github.com/')
154160
for token in bad_tokens:
155161
s = self.build_session()
156162
s.token_auth(token)
157-
assert 'Authorization' not in s.headers
163+
pr = s.prepare_request(req)
164+
assert 'Authorization' not in pr.headers
165+
166+
def test_token_auth_with_netrc_works(self, tmpdir):
167+
168+
token = "my-valid-token"
169+
s = self.build_session()
170+
s.token_auth(token)
171+
172+
netrc_contents = (
173+
"machine api.github.com\n"
174+
"login sigmavirus24\n"
175+
"password invalid_token_for_test_verification\n"
176+
)
177+
# cover testing netrc behaviour on different OSs
178+
dotnetrc = tmpdir.join(".netrc")
179+
dotnetrc.write(netrc_contents)
180+
dashnetrc = tmpdir.join("_netrc")
181+
dashnetrc.write(netrc_contents)
182+
183+
with mock.patch.dict('os.environ', {'HOME': str(tmpdir)}):
184+
# prepare_request triggers reading of .netrc files
185+
pr = s.prepare_request(
186+
requests.Request(
187+
'GET', 'https://api.github.com/users/sigmavirus24')
188+
)
189+
auth_header = pr.headers['Authorization']
190+
assert auth_header == 'token {0}'.format(token)
158191

159192
def test_two_factor_auth_callback_handles_None(self):
160193
s = self.build_session()
@@ -214,13 +247,15 @@ def test_no_auth(self):
214247
"""Verify that no_auth removes existing authentication."""
215248
s = self.build_session()
216249
s.basic_auth('user', 'password')
217-
s.headers['Authorization'] = 'token foobarbogus'
250+
req = requests.Request('GET', 'https://api.github.com/')
218251

219252
with s.no_auth():
220-
assert 'Authentication' not in s.headers
253+
pr = s.prepare_request(req)
254+
assert 'Authorization' not in pr.headers
221255
assert s.auth is None
222256

223-
assert s.headers['Authorization'] == 'token foobarbogus'
257+
pr = s.prepare_request(req)
258+
assert 'Authorization' in pr.headers
224259
assert s.auth == ('user', 'password')
225260

226261
def test_retrieve_client_credentials_when_set(self):

0 commit comments

Comments
 (0)
0