This commit is contained in:
Iliyan Angelov
2025-09-14 23:24:25 +03:00
commit c67067a2a4
71311 changed files with 6800714 additions and 0 deletions

View File

@@ -0,0 +1,48 @@
from allauth.socialaccount import app_settings
from allauth.socialaccount.providers.base import ProviderAccount
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
class GitHubAccount(ProviderAccount):
def get_profile_url(self):
return self.account.extra_data.get("html_url")
def get_avatar_url(self):
return self.account.extra_data.get("avatar_url")
def to_str(self):
dflt = super(GitHubAccount, self).to_str()
return next(
value
for value in (
self.account.extra_data.get("name", None),
self.account.extra_data.get("login", None),
dflt,
)
if value is not None
)
class GitHubProvider(OAuth2Provider):
id = "github"
name = "GitHub"
account_class = GitHubAccount
def get_default_scope(self):
scope = []
if app_settings.QUERY_EMAIL:
scope.append("user:email")
return scope
def extract_uid(self, data):
return str(data["id"])
def extract_common_fields(self, data):
return dict(
email=data.get("email"),
username=data.get("login"),
name=data.get("name"),
)
provider_classes = [GitHubProvider]

View File

@@ -0,0 +1,60 @@
from allauth.socialaccount.models import SocialAccount
from allauth.socialaccount.tests import OAuth2TestsMixin
from allauth.tests import MockedResponse, TestCase
from .provider import GitHubProvider
class GitHubTests(OAuth2TestsMixin, TestCase):
provider_id = GitHubProvider.id
def get_mocked_response(self):
return MockedResponse(
200,
"""
{
"type":"User",
"organizations_url":"https://api.github.com/users/pennersr/orgs",
"gists_url":"https://api.github.com/users/pennersr/gists{/gist_id}",
"received_events_url":"https://api.github.com/users/pennersr/received_events",
"gravatar_id":"8639768262b8484f6a3380f8db2efa5b",
"followers":16,
"blog":"http://www.intenct.info",
"avatar_url":"https://secure.gravatar.com/avatar/8639768262b8484f6a3380f8db2efa5b?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png",
"login":"pennersr",
"created_at":"2010-02-10T12:50:51Z",
"company":"IntenCT",
"subscriptions_url":"https://api.github.com/users/pennersr/subscriptions",
"public_repos":14,
"hireable":false,
"url":"https://api.github.com/users/pennersr",
"public_gists":0,
"starred_url":"https://api.github.com/users/pennersr/starred{/owner}{/repo}",
"html_url":"https://github.com/pennersr",
"location":"The Netherlands",
"bio":null,
"name":"Raymond Penners",
"repos_url":"https://api.github.com/users/pennersr/repos",
"followers_url":"https://api.github.com/users/pennersr/followers",
"id":201022,
"following":0,
"email":"raymond.penners@intenct.nl",
"events_url":"https://api.github.com/users/pennersr/events{/privacy}",
"following_url":"https://api.github.com/users/pennersr/following"
}""",
)
def test_account_name_null(self):
"""String conversion when GitHub responds with empty name"""
data = """{
"type": "User",
"id": 201022,
"login": "pennersr",
"name": null
}"""
self.login(MockedResponse(200, data))
socialaccount = SocialAccount.objects.get(uid="201022")
self.assertIsNone(socialaccount.extra_data.get("name"))
account = socialaccount.get_provider_account()
self.assertIsNotNone(account.to_str())
self.assertEqual(account.to_str(), "pennersr")

View File

@@ -0,0 +1,6 @@
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
from .provider import GitHubProvider
urlpatterns = default_urlpatterns(GitHubProvider)

View File

@@ -0,0 +1,55 @@
import requests
from allauth.socialaccount import app_settings
from allauth.socialaccount.providers.github.provider import GitHubProvider
from allauth.socialaccount.providers.oauth2.views import (
OAuth2Adapter,
OAuth2CallbackView,
OAuth2LoginView,
)
class GitHubOAuth2Adapter(OAuth2Adapter):
provider_id = GitHubProvider.id
settings = app_settings.PROVIDERS.get(provider_id, {})
if "GITHUB_URL" in settings:
web_url = settings.get("GITHUB_URL").rstrip("/")
api_url = "{0}/api/v3".format(web_url)
else:
web_url = "https://github.com"
api_url = "https://api.github.com"
access_token_url = "{0}/login/oauth/access_token".format(web_url)
authorize_url = "{0}/login/oauth/authorize".format(web_url)
profile_url = "{0}/user".format(api_url)
emails_url = "{0}/user/emails".format(api_url)
def complete_login(self, request, app, token, **kwargs):
headers = {"Authorization": "token {}".format(token.token)}
resp = requests.get(self.profile_url, headers=headers)
resp.raise_for_status()
extra_data = resp.json()
if app_settings.QUERY_EMAIL and not extra_data.get("email"):
extra_data["email"] = self.get_email(headers)
return self.get_provider().sociallogin_from_response(request, extra_data)
def get_email(self, headers):
email = None
resp = requests.get(self.emails_url, headers=headers)
resp.raise_for_status()
emails = resp.json()
if resp.status_code == 200 and emails:
email = emails[0]
primary_emails = [
e for e in emails if not isinstance(e, dict) or e.get("primary")
]
if primary_emails:
email = primary_emails[0]
if isinstance(email, dict):
email = email.get("email", "")
return email
oauth2_login = OAuth2LoginView.adapter_view(GitHubOAuth2Adapter)
oauth2_callback = OAuth2CallbackView.adapter_view(GitHubOAuth2Adapter)