Repository: liberapay/liberapay.com

Issue:
Connecting GitHub organization fails when membership is private
A private member of a GitHub organization can't connect it to a Liberapay account, because we stopped asking for the `read:org` permission in #1484. The error page shown to the user, for example <https://liberapay.com/on/github/liberapay/failure.html?error=CantReadMembership>, hasn't been updated accordingly.


Discussion:
I can confirm setting the Github team membership to public fixes the issue. Thank you very much for investigating this!

Resolving patch:
diff --git a/liberapay/elsewhere/_base.py b/liberapay/elsewhere/_base.py
--- a/liberapay/elsewhere/_base.py
+++ b/liberapay/elsewhere/_base.py
@@ -374,14 +374,18 @@ def get_repos(self, account, page_url=None, sess=None, refresh=True):
         r = self.api_get(account.domain, page_url, sess=sess)
         repos, count, pages_urls = self.api_paginator(r, self.api_parser(r))
         repos = [self.extract_repo_info(repo, account.domain) for repo in repos]
-        if '{user_name}' in self.api_repos_path and repos and repos[0].owner_id != account.user_id:
+        if '{user_id}' in self.api_repos_path:
+            for repo in repos:
+                if repo.owner_id is None:
+                    repo.owner_id = account.user_id
+        elif '{user_name}' in self.api_repos_path and repos and repos[0].owner_id != account.user_id:
             # https://hackerone.com/reports/452920
             if not refresh:
                 raise TokenExpiredError()
             from liberapay.models.account_elsewhere import UnableToRefreshAccount
             try:
                 account = account.refresh_user_info()
-            except UnableToRefreshAccount:
+            except (UnableToRefreshAccount, UserNotFound):
                 raise TokenExpiredError()
             # Note: we can't pass the page_url below, because it contains the old user_name
             return self.get_repos(account, page_url=None, sess=sess, refresh=False)
@@ -442,6 +446,9 @@ def get_credentials_from_db(self, domain):
                AND domain = %s
         """, (self.name, domain))
 
+    def get_CantReadMembership_url(self, account):
+        return ''
+
 
 class PlatformOAuth1(Platform):
 
diff --git a/liberapay/elsewhere/github.py b/liberapay/elsewhere/github.py
--- a/liberapay/elsewhere/github.py
+++ b/liberapay/elsewhere/github.py
@@ -56,8 +56,8 @@ class GitHub(PlatformOAuth2):
     x_repo_owner_id = key('owner', clean=lambda d: d['id'])
     x_repo_extra_info_drop = drop_keys(lambda k: k.endswith('_url'))
 
-    def get_CantReadMembership_url(self, **kw):
-        return 'https://github.com/settings/connections/applications/'+self.api_key
+    def get_CantReadMembership_url(self, account):
+        return 'https://github.com/orgs/%s/people' % account.user_name
 
     def is_team_member(self, org_name, sess, account):
         org_name = org_name.lower()
diff --git a/liberapay/elsewhere/gitlab.py b/liberapay/elsewhere/gitlab.py
--- a/liberapay/elsewhere/gitlab.py
+++ b/liberapay/elsewhere/gitlab.py
@@ -56,3 +56,4 @@ class GitLab(PlatformOAuth2):
     x_repo_last_update = key('last_activity_at')
     x_repo_is_fork = key('forked_from_project', clean=bool)
     x_repo_stars_count = key('star_count')
+    x_repo_owner_id = key('owner', clean=lambda d: d['id'])  # not included in responses to unauthenticated requests


Test patch: