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,33 @@
from allauth.socialaccount.providers.base import ProviderAccount
from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider
class BitbucketOAuth2Account(ProviderAccount):
def get_profile_url(self):
return self.account.extra_data.get("links", {}).get("html", {}).get("href")
def get_avatar_url(self):
return self.account.extra_data.get("links", {}).get("avatar", {}).get("href")
def to_str(self):
dflt = super(BitbucketOAuth2Account, self).to_str()
return self.account.extra_data.get("display_name", dflt)
class BitbucketOAuth2Provider(OAuth2Provider):
id = "bitbucket_oauth2"
name = "Bitbucket"
account_class = BitbucketOAuth2Account
def extract_uid(self, data):
return data["username"]
def extract_common_fields(self, data):
return dict(
email=data.get("email"),
username=data.get("username"),
name=data.get("display_name"),
)
provider_classes = [BitbucketOAuth2Provider]

View File

@@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from unittest import mock
from django.test.utils import override_settings
from allauth.socialaccount.models import SocialAccount
from allauth.socialaccount.tests import OAuth2TestsMixin
from allauth.tests import MockedResponse, TestCase, patch
from .provider import BitbucketOAuth2Provider
@override_settings(SOCIALACCOUNT_QUERY_EMAIL=True, SOCIALACCOUNT_STORE_TOKENS=True)
class BitbucketOAuth2Tests(OAuth2TestsMixin, TestCase):
provider_id = BitbucketOAuth2Provider.id
response_data = """
{
"created_on": "2011-12-20T16:34:07.132459+00:00",
"display_name": "tutorials account",
"links": {
"avatar": {
"href":
"https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Nov/25/tutorials-avatar-1563784409-6_avatar.png"
},
"followers": {
"href":
"https://api.bitbucket.org/2.0/users/tutorials/followers"
},
"following": {
"href":
"https://api.bitbucket.org/2.0/users/tutorials/following"
},
"html": {
"href": "https://bitbucket.org/tutorials"
},
"repositories": {
"href":
"https://api.bitbucket.org/2.0/repositories/tutorials"
},
"self": {
"href": "https://api.bitbucket.org/2.0/users/tutorials"
}
},
"location": "Santa Monica, CA",
"type": "user",
"username": "tutorials",
"uuid": "{c788b2da-b7a2-404c-9e26-d3f077557007}",
"website": "https://tutorials.bitbucket.org/"
}
""" # noqa
email_response_data = """
{
"page": 1,
"pagelen": 10,
"size": 1,
"values": [
{
"email": "tutorials@bitbucket.org",
"is_confirmed": true,
"is_primary": true,
"links": {
"self": {
"href":
"https://api.bitbucket.org/2.0/user/emails/tutorials@bitbucket.org"
}
},
"type": "email"
},
{
"email": "tutorials+secondary@bitbucket.org",
"is_confirmed": true,
"is_primary": true,
"links": {
"self": {
"href":
"https://api.bitbucket.org/2.0/user/emails/tutorials+secondary@bitbucket.org"
}
},
"type": "email"
}
]
}
""" # noqa
def setUp(self):
super(BitbucketOAuth2Tests, self).setUp()
self.mocks = {
"requests": patch(
"allauth.socialaccount.providers.bitbucket_oauth2.views.requests"
)
}
self.patches = {name: mocked.start() for (name, mocked) in self.mocks.items()}
self.patches["requests"].get.side_effect = [
MockedResponse(200, self.response_data),
MockedResponse(200, self.email_response_data),
]
def tearDown(self):
for _, mocked in self.mocks.items():
mocked.stop()
def get_mocked_response(self):
return [MockedResponse(200, self.response_data)]
def test_account_tokens(self, multiple_login=False):
if multiple_login:
self.patches["requests"].get.side_effect = [
MockedResponse(200, self.response_data),
MockedResponse(200, self.email_response_data),
MockedResponse(200, self.response_data),
MockedResponse(200, self.email_response_data),
]
super(BitbucketOAuth2Tests, self).test_account_tokens(multiple_login)
calls = [
mock.call("https://api.bitbucket.org/2.0/user", params=mock.ANY),
mock.call("https://api.bitbucket.org/2.0/user/emails", params=mock.ANY),
]
if multiple_login:
calls.extend(
[
mock.call("https://api.bitbucket.org/2.0/user", params=mock.ANY),
mock.call(
"https://api.bitbucket.org/2.0/user/emails",
params=mock.ANY,
),
]
)
self.patches["requests"].get.assert_has_calls(calls)
def test_provider_account(self):
self.login(self.get_mocked_response())
socialaccount = SocialAccount.objects.get(uid="tutorials")
self.assertEqual(socialaccount.user.username, "tutorials")
self.assertEqual(socialaccount.user.email, "tutorials@bitbucket.org")
account = socialaccount.get_provider_account()
self.assertEqual(account.to_str(), "tutorials account")
self.assertEqual(account.get_profile_url(), "https://bitbucket.org/tutorials")
self.assertEqual(
account.get_avatar_url(),
"https://bitbucket-assetroot.s3.amazonaws.com/c/photos/2013/Nov/25/tutorials-avatar-1563784409-6_avatar.png", # noqa
)
self.patches["requests"].get.assert_has_calls(
[
mock.call("https://api.bitbucket.org/2.0/user", params=mock.ANY),
mock.call(
"https://api.bitbucket.org/2.0/user/emails",
params=mock.ANY,
),
]
)

View File

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

View File

@@ -0,0 +1,43 @@
import requests
from allauth.socialaccount import app_settings
from allauth.socialaccount.providers.oauth2.views import (
OAuth2Adapter,
OAuth2CallbackView,
OAuth2LoginView,
)
from .provider import BitbucketOAuth2Provider
class BitbucketOAuth2Adapter(OAuth2Adapter):
provider_id = BitbucketOAuth2Provider.id
access_token_url = "https://bitbucket.org/site/oauth2/access_token"
authorize_url = "https://bitbucket.org/site/oauth2/authorize"
profile_url = "https://api.bitbucket.org/2.0/user"
emails_url = "https://api.bitbucket.org/2.0/user/emails"
def complete_login(self, request, app, token, **kwargs):
resp = requests.get(self.profile_url, params={"access_token": token.token})
extra_data = resp.json()
if app_settings.QUERY_EMAIL and not extra_data.get("email"):
extra_data["email"] = self.get_email(token)
return self.get_provider().sociallogin_from_response(request, extra_data)
def get_email(self, token):
"""Fetches email address from email API endpoint"""
resp = requests.get(self.emails_url, params={"access_token": token.token})
emails = resp.json().get("values", [])
email = ""
try:
email = emails[0].get("email")
primary_emails = [e for e in emails if e.get("is_primary", False)]
email = primary_emails[0].get("email")
except (IndexError, TypeError, KeyError):
return ""
finally:
return email
oauth_login = OAuth2LoginView.adapter_view(BitbucketOAuth2Adapter)
oauth_callback = OAuth2CallbackView.adapter_view(BitbucketOAuth2Adapter)