Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix auth issues with Installation.get_repos #2547

Merged
merged 2 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions github/Installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import github.Plan
import github.Repository
import github.UserKey
from github.Auth import AppAuth

from . import Consts

Expand All @@ -46,6 +47,16 @@ class Installation(github.GithubObject.NonCompletableGithubObject):
This class represents Installations. The reference can be found here https://docs.github.com/en/rest/reference/apps#installations
"""

def __init__(self, requester, headers, attributes, completed):
super().__init__(requester, headers, attributes, completed)

auth = self._requester.auth if self._requester is not None else None
# Usually, an Installation is created from a Requester with App authentication
if isinstance(auth, AppAuth):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if it's not? Or there is no Auth? Can you have anonymous access to an installation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The auth should neither be None nor not of type AppAuth, but just in case it is, I don't want to change existing behaviour of user code, which should see an error response by the API.

Maybe there is a valid code path that creates an Installation without auth, because it never uses the requester.

# But the installation has to authenticate as an installation (e.g. for get_repos())
auth = auth.get_installation_auth(self.id, requester=self._requester)
self._requester = self._requester.withAuth(auth)

def __repr__(self):
return self.get__repr__({"id": self._id.value})

Expand Down
4 changes: 4 additions & 0 deletions github/Requester.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ def __init__(
def base_url(self):
return self.__base_url

@property
def auth(self):
return self.__auth

def withAuth(self, auth):
"""
Create a new requester instance with identical configuration but the given authentication method.
Expand Down
2 changes: 2 additions & 0 deletions github/Requester.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ class Requester:
) -> None: ...
@property
def base_url(self) -> str: ...
@property
def auth(self) -> Optional[Auth]: ...
def withAuth(self, auth: Optional[Auth]) -> Requester: ...
def _initializeDebugFeature(self) -> None: ...
def check_me(self, obj: GithubObject) -> None: ...
Expand Down
46 changes: 46 additions & 0 deletions tests/Installation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
############################ Copyrights and license ############################
# #
# Copyright 2023 Enrico Minack <github@enrico.minack.dev> #
# #
# This file is part of PyGithub. #
# http://pygithub.readthedocs.io/ #
# #
# PyGithub is free software: you can redistribute it and/or modify it under #
# the terms of the GNU Lesser General Public License as published by the Free #
# Software Foundation, either version 3 of the License, or (at your option) #
# any later version. #
# #
# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY #
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS #
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more #
# details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
# #
################################################################################

import github
from github.Auth import AppAuth

from . import Framework, GithubIntegration


class Installation(Framework.BasicTestCase):
def setUp(self):
super().setUp()
app_id = 36541767
private_key = GithubIntegration.PRIVATE_KEY
self.auth = AppAuth(app_id, private_key)
self.integration = github.GithubIntegration(auth=self.auth)
self.installations = list(self.integration.get_installations())

def testGetRepos(self):
self.assertEqual(len(self.installations), 1)
installation = self.installations[0]

repos = list(installation.get_repos())
self.assertEqual(len(repos), 2)
self.assertListEqual(
[repo.full_name for repo in repos], ["EnricoMi/sandbox", "EnricoMi/python"]
)
11 changes: 11 additions & 0 deletions tests/ReplayData/Installation.setUp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
https
GET
api.github.com
None
/app/installations
{'Accept': 'application/vnd.github.machine-man-preview+json', 'Authorization': 'Bearer jwt_removed', 'User-Agent': 'PyGithub/Python'}
None
200
[('Server', 'GitHub.com'), ('Date', 'Thu, 08 Jun 2023 07:39:24 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('Cache-Control', 'public, max-age=60, s-maxage=60'), ('Vary', 'Accept, Accept-Encoding, Accept, X-Requested-With'), ('ETag', 'W/"b272db57fded7547562b4a357681c6c84054e8fb5169b32206e833bbe7f542e5"'), ('X-GitHub-Media-Type', 'github.v3; param=machine-man-preview; format=json'), ('x-github-api-version-selected', '2022-11-28'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '0'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', 'D58E:1978:151E09E:1565462:648185AC')]
[{"id":36541767,"account":{"login":"EnricoMi","id":44700269,"node_id":"MDQ6VXNlcjQ0NzAwMjY5","avatar_url":"https://avatars.githubusercontent.com/u/44700269?v=4","gravatar_id":"","url":"https://api.github.com/users/EnricoMi","html_url":"https://github.com/EnricoMi","followers_url":"https://api.github.com/users/EnricoMi/followers","following_url":"https://api.github.com/users/EnricoMi/following{/other_user}","gists_url":"https://api.github.com/users/EnricoMi/gists{/gist_id}","starred_url":"https://api.github.com/users/EnricoMi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/EnricoMi/subscriptions","organizations_url":"https://api.github.com/users/EnricoMi/orgs","repos_url":"https://api.github.com/users/EnricoMi/repos","events_url":"https://api.github.com/users/EnricoMi/events{/privacy}","received_events_url":"https://api.github.com/users/EnricoMi/received_events","type":"User","site_admin":false},"repository_selection":"selected","access_tokens_url":"https://api.github.com/app/installations/36541767/access_tokens","repositories_url":"https://api.github.com/installation/repositories","html_url":"https://github.com/settings/installations/36541767","app_id":319953,"app_slug":"publish-test-results","target_id":44700269,"target_type":"User","permissions":{"checks":"write","issues":"read","contents":"read","metadata":"read","pull_requests":"write"},"events":[],"created_at":"2023-04-17T16:18:05.000Z","updated_at":"2023-06-08T07:38:12.000Z","single_file_name":null,"has_multiple_single_files":false,"single_file_paths":[],"suspended_by":null,"suspended_at":null}]