diff --git a/ghcloneall.py b/ghcloneall.py index 5c3aceb..21d36d0 100755 --- a/ghcloneall.py +++ b/ghcloneall.py @@ -423,6 +423,15 @@ def list_gists(self, user, pattern=None): # - exclude private gists (if g['public']) return sorted(map(Repo.from_gist, gists), key=attrgetter('name')) + def _verify_user_token(self, user): + # Verify that the user and token match + user_data, _ = get_json_and_links('https://api.github.com/user', + session=self.session) + if user_data.get('login') == user: + return + raise Error('The github_user specified ({}) ' + 'does not match the token used.'.format(user)) + def list_repos(self, user=None, organization=None, pattern=None, include_archived=False, include_forks=False, include_private=True, include_disabled=True): @@ -443,6 +452,7 @@ def list_repos(self, user=None, organization=None, pattern=None, elif user and not organization: owner = user if include_private and self.has_auth_token: + self._verify_user_token(user) # users/$name/repos does not include private repos, so # we have to query for the repos owned by the current # user instead. This only works if the current token diff --git a/tests.py b/tests.py index dd41ace..4e8b091 100644 --- a/tests.py +++ b/tests.py @@ -37,6 +37,8 @@ def raise_for_status(self): class MockRequestGet: + user_endpoint = 'https://api.github.com/user' + def __init__(self): self.responses = {} self.not_found = MockResponse( @@ -46,6 +48,15 @@ def __init__(self): def update(self, responses): self.responses.update(responses) + def set_user(self, user): + if user is None: + if self.user_endpoint in self.responses: + del self.responses[self.user_endpoint] + else: + self.responses[self.user_endpoint] = MockResponse( + json={'login': user}, + ) + def __call__(self, url, headers=None): return self.responses.get(url, self.not_found) @@ -55,6 +66,7 @@ def mock_requests_get(monkeypatch): mock_get = MockRequestGet() monkeypatch.setattr(requests, 'get', mock_get) monkeypatch.setattr(requests.Session, 'get', mock_get) + mock_get.set_user(None) return mock_get @@ -1374,10 +1386,12 @@ def test_main_no_org_gists(monkeypatch, capsys): ) -def test_main_run_error_handling_with_private_token(monkeypatch, capsys): +def test_main_run_error_handling_with_private_token( + monkeypatch, mock_requests_get, capsys): monkeypatch.setattr(sys, 'argv', [ 'ghcloneall', '--user', 'mgedmin', '--github-token', 'xyzzy', ]) + mock_requests_get.set_user('mgedmin') with pytest.raises(SystemExit) as ctx: ghcloneall.main() assert str(ctx.value) == ( @@ -1427,6 +1441,7 @@ def test_main_run_with_token(monkeypatch, mock_requests_get, capsys): 'ghcloneall', '--user', 'mgedmin', '--concurrency=1', '--github-token', 'fake-token', ]) + mock_requests_get.set_user('mgedmin') mock_requests_get.update(mock_multi_page_api_responses( url='https://api.github.com/user/repos?affiliation=owner', pages=[ @@ -1446,6 +1461,21 @@ def test_main_run_with_token(monkeypatch, mock_requests_get, capsys): ) +def test_main_run_with_mismatched_token(monkeypatch, mock_requests_get, + capsys): + monkeypatch.setattr(sys, 'argv', [ + 'ghcloneall', '--user', 'test_user', '--concurrency=1', + '--github-token', 'fake-token', + ]) + mock_requests_get.set_user('some-other-user') + with pytest.raises(SystemExit) as ctx: + ghcloneall.main() + assert str(ctx.value) == ( + 'The github_user specified (test_user) ' + 'does not match the token used.' + ) + + def test_main_run_private_without_token(monkeypatch, mock_requests_get, capsys): monkeypatch.setattr(sys, 'argv', [