diff --git a/readthedocs/allauth/providers/githubapp/__init__.py b/readthedocs/allauth/providers/githubapp/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/readthedocs/allauth/providers/githubapp/provider.py b/readthedocs/allauth/providers/githubapp/provider.py new file mode 100644 index 00000000000..cf5958a7826 --- /dev/null +++ b/readthedocs/allauth/providers/githubapp/provider.py @@ -0,0 +1,10 @@ +from allauth.socialaccount.providers.github.provider import GitHubProvider +from readthedocs.allauth.providers.githubapp.views import GitHubAppOAuth2Adapter + + +class GitHubAppProvider(GitHubProvider): + id = "githubapp" + name = "GitHub App" + oauth2_adapter_class = GitHubAppOAuth2Adapter + +provider_classes = [GitHubAppProvider] diff --git a/readthedocs/allauth/providers/githubapp/urls.py b/readthedocs/allauth/providers/githubapp/urls.py new file mode 100644 index 00000000000..3ab15feaae2 --- /dev/null +++ b/readthedocs/allauth/providers/githubapp/urls.py @@ -0,0 +1,6 @@ +from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns + +from readthedocs.allauth.providers.githubapp.provider import GitHubAppProvider + + +urlpatterns = default_urlpatterns(GitHubAppProvider) diff --git a/readthedocs/allauth/providers/githubapp/views.py b/readthedocs/allauth/providers/githubapp/views.py new file mode 100644 index 00000000000..a5d1377c648 --- /dev/null +++ b/readthedocs/allauth/providers/githubapp/views.py @@ -0,0 +1,13 @@ +from allauth.socialaccount.providers.github.views import GitHubOAuth2Adapter + +from allauth.socialaccount.providers.oauth2.views import ( + OAuth2CallbackView, + OAuth2LoginView, +) + +class GitHubAppOAuth2Adapter(GitHubOAuth2Adapter): + provider_id = 'githubapp' + + +oauth2_login = OAuth2LoginView.adapter_view(GitHubAppOAuth2Adapter) +oauth2_callback = OAuth2CallbackView.adapter_view(GitHubAppOAuth2Adapter) diff --git a/readthedocs/core/adapters.py b/readthedocs/core/adapters.py index 56edf345691..c2f8005271d 100644 --- a/readthedocs/core/adapters.py +++ b/readthedocs/core/adapters.py @@ -1,9 +1,14 @@ """Allauth overrides.""" +from allauth.socialaccount.adapter import DefaultSocialAccountAdapter +from allauth.socialaccount.models import SocialAccount +from allauth.socialaccount.providers.github.provider import GitHubProvider + import structlog from allauth.account.adapter import DefaultAccountAdapter from django.utils.encoding import force_str +from readthedocs.allauth.providers.githubapp.provider import GitHubAppProvider from readthedocs.core.utils import send_email_from_object from readthedocs.invitations.models import Invitation @@ -50,3 +55,16 @@ def save_user(self, request, user, form, commit=True): invitation.delete() else: log.info("Invitation not found", invitation_pk=invitation_pk) + + +class SocialAccountAdapter(DefaultSocialAccountAdapter): + + def pre_social_login(self, request, sociallogin): + provider = sociallogin.account.get_provider() + if provider.id == GitHubAppProvider.id and not sociallogin.is_existing: + social_ccount = SocialAccount.objects.filter( + provider=GitHubProvider.id, + uid=sociallogin.account.uid, + ).first() + if social_ccount: + sociallogin.connect(request, social_ccount.user) diff --git a/readthedocs/settings/base.py b/readthedocs/settings/base.py index b98cc1ea04f..7e7e6ca9038 100644 --- a/readthedocs/settings/base.py +++ b/readthedocs/settings/base.py @@ -2,19 +2,17 @@ import os import re -import subprocess import socket +import subprocess import structlog - from celery.schedules import crontab +from corsheaders.defaults import default_headers +from django.conf.global_settings import PASSWORD_HASHERS +from readthedocs.builds import constants_docker from readthedocs.core.logs import shared_processors -from corsheaders.defaults import default_headers from readthedocs.core.settings import Settings -from readthedocs.builds import constants_docker - -from django.conf.global_settings import PASSWORD_HASHERS try: import readthedocsext.cdn # noqa @@ -73,7 +71,7 @@ def _show_debug_toolbar(request): # It's a "known issue/bug" and there is no solution as far as we can tell. "debug_toolbar.panels.sql.SQLPanel", "debug_toolbar.panels.templates.TemplatesPanel", - ] + ], } @property @@ -294,6 +292,7 @@ def INSTALLED_APPS(self): # noqa "allauth.account", "allauth.socialaccount", "allauth.socialaccount.providers.github", + "readthedocs.allauth.providers.githubapp", "allauth.socialaccount.providers.gitlab", "allauth.socialaccount.providers.bitbucket_oauth2", "allauth.mfa", @@ -679,6 +678,7 @@ def DOCKER_LIMITS(self): # Allauth ACCOUNT_ADAPTER = "readthedocs.core.adapters.AccountAdapter" + SOCIALACCOUNT_ADAPTER = 'readthedocs.core.adapters.SocialAccountAdapter' ACCOUNT_EMAIL_REQUIRED = True # By preventing enumeration, we will always send an email, # even if the email is not registered, that's hurting @@ -709,6 +709,12 @@ def DOCKER_LIMITS(self): "repo:status", ], }, + "githubapp": { + "APPS": [ + {"client_id": "123", "secret": "456", "key": ""}, + ], + "SCOPE": [], + }, "gitlab": { "APPS": [ {"client_id": "123", "secret": "456", "key": ""},