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,63 @@
from django.urls import reverse
from django.utils.http import urlencode
from allauth.socialaccount.providers.base import Provider, ProviderAccount
class DraugiemAccount(ProviderAccount):
def get_avatar_url(self):
ret = None
pic_small_url = self.account.extra_data.get("img")
pic_icon_url = self.account.extra_data.get("imgi")
pic_medium_url = self.account.extra_data.get("imgm")
pic_large_url = self.account.extra_data.get("imgl")
if pic_large_url:
return pic_large_url
elif pic_medium_url:
return pic_medium_url
elif pic_icon_url:
return pic_icon_url
elif pic_small_url:
return pic_small_url
else:
return ret
def to_str(self):
default = super(DraugiemAccount, self).to_str()
name = self.account.extra_data.get("name")
surname = self.account.extra_data.get("surnname")
if name and surname:
return "%s %s" % (name, surname)
return default
class DraugiemProvider(Provider):
id = "draugiem"
name = "Draugiem"
account_class = DraugiemAccount
def get_login_url(self, request, **kwargs):
url = reverse(self.id + "_login")
if kwargs:
url = url + "?" + urlencode(kwargs)
return url
def extract_uid(self, data):
return str(data["uid"])
def extract_common_fields(self, data):
uid = self.extract_uid(data)
user_data = data["users"][uid]
return dict(
first_name=user_data.get("name"),
last_name=user_data.get("surname"),
)
def extract_extra_data(self, data):
uid = self.extract_uid(data)
return data["users"][uid]
provider_classes = [DraugiemProvider]

View File

@@ -0,0 +1,136 @@
from hashlib import md5
from django.contrib.auth.models import User
from django.test import RequestFactory
from django.urls import reverse
from django.utils.http import urlencode
from allauth import app_settings
from allauth.socialaccount.models import SocialApp, SocialToken
from allauth.tests import Mock, TestCase, patch
from . import views
from .provider import DraugiemProvider
class DraugiemTests(TestCase):
def setUp(self):
# workaround to create a session. see:
# https://code.djangoproject.com/ticket/11475
User.objects.create_user(
"anakin", "skywalker@deathstar.example.com", "s1thrul3s"
)
self.client.login(username="anakin", password="s1thrul3s")
app = SocialApp.objects.create(
provider=DraugiemProvider.id,
name=DraugiemProvider.id,
client_id="app123id",
key=DraugiemProvider.id,
secret="dummy",
)
request = RequestFactory().get("/")
self.provider = app.get_provider(request)
if app_settings.SITES_ENABLED:
from django.contrib.sites.models import Site
app.sites.add(Site.objects.get_current())
self.app = app
def get_draugiem_login_response(self):
"""
Sample Draugiem.lv response
"""
return {
"apikey": "12345",
"uid": "42",
"users": {
"42": {
"age": "266",
"imgl": "http://cdn.memegenerator.net/instances/500x/23395689.jpg",
"surname": "Skywalker",
"url": "/user/42/",
"imgi": "http://cdn.memegenerator.net/instances/500x/23395689.jpg",
"nick": "Sky Guy",
"created": "09.11.1812 11:26:15",
"deleted": "false",
"imgm": "http://cdn.memegenerator.net/instances/500x/23395689.jpg",
"sex": "M",
"type": "User_Default",
"uid": "42",
"place": "London",
"emailHash": "3f198f21434gfd2f2b4rs05939shk93f3815bc6aa",
"name": "Anakin",
"adult": "1",
"birthday": "1750-09-13",
"img": "http://cdn.memegenerator.net/instances/500x/23395689.jpg",
}
},
}
def get_socialaccount(self, response, token):
"""
Returns SocialLogin based on the data from the request
"""
request = Mock()
login = self.provider.sociallogin_from_response(request, response)
login.token = token
return login
def mock_socialaccount_state(self):
"""
SocialLogin depends on Session state - a tuple of request
params and a random string
"""
session = self.client.session
session["socialaccount_state"] = (
{"process": "login", "scope": "", "auth_params": ""},
"12345",
)
session.save()
def test_login_redirect(self):
response = self.client.get(reverse(views.login))
redirect_url = reverse(views.callback)
full_redirect_url = "http://testserver" + redirect_url
secret = self.app.secret + full_redirect_url
redirect_url_hash = md5(secret.encode("utf-8")).hexdigest()
params = {
"app": self.app.client_id,
"hash": redirect_url_hash,
"redirect": full_redirect_url,
}
self.assertRedirects(
response,
"%s?%s" % (views.AUTHORIZE_URL, urlencode(params)),
fetch_redirect_response=False,
)
def test_callback_no_auth_status(self):
response = self.client.get(reverse(views.callback))
self.assertTemplateUsed(response, "socialaccount/authentication_error.html")
def test_callback_invalid_auth_status(self):
response = self.client.get(reverse(views.callback), {"dr_auth_status": "fail"})
self.assertTemplateUsed(response, "socialaccount/authentication_error.html")
def test_callback(self):
with patch(
"allauth.socialaccount.providers.draugiem.views.draugiem_complete_login"
) as draugiem_complete_login:
self.mock_socialaccount_state()
response_json = self.get_draugiem_login_response()
token = SocialToken(app=self.app, token=response_json["apikey"])
login = self.get_socialaccount(response_json, token)
draugiem_complete_login.return_value = login
response = self.client.get(
reverse(views.callback),
{"dr_auth_status": "ok", "dr_auth_code": "42"},
)
self.assertRedirects(
response, "/accounts/profile/", fetch_redirect_response=False
)

View File

@@ -0,0 +1,9 @@
from django.urls import path
from . import views
urlpatterns = [
path("draugiem/login/", views.login, name="draugiem_login"),
path("draugiem/callback/", views.callback, name="draugiem_callback"),
]

View File

@@ -0,0 +1,93 @@
import requests
from hashlib import md5
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.http import urlencode
from django.views.decorators.csrf import csrf_exempt
from allauth.socialaccount.adapter import get_adapter
from allauth.socialaccount.helpers import (
complete_social_login,
render_authentication_error,
)
from allauth.socialaccount.models import SocialLogin, SocialToken
from ..base import AuthError
from .provider import DraugiemProvider
class DraugiemApiError(Exception):
pass
ACCESS_TOKEN_URL = "http://api.draugiem.lv/json"
AUTHORIZE_URL = "http://api.draugiem.lv/authorize"
def login(request):
app = get_adapter().get_app(request, DraugiemProvider.id)
redirect_url = request.build_absolute_uri(reverse(callback))
redirect_url_hash = md5((app.secret + redirect_url).encode("utf-8")).hexdigest()
params = {
"app": app.client_id,
"hash": redirect_url_hash,
"redirect": redirect_url,
}
SocialLogin.stash_state(request)
return HttpResponseRedirect("%s?%s" % (AUTHORIZE_URL, urlencode(params)))
@csrf_exempt
def callback(request):
if "dr_auth_status" not in request.GET:
return render_authentication_error(
request, DraugiemProvider.id, error=AuthError.UNKNOWN
)
if request.GET["dr_auth_status"] != "ok":
return render_authentication_error(
request, DraugiemProvider.id, error=AuthError.DENIED
)
if "dr_auth_code" not in request.GET:
return render_authentication_error(
request, DraugiemProvider.id, error=AuthError.UNKNOWN
)
ret = None
auth_exception = None
try:
app = get_adapter().get_app(request, DraugiemProvider.id)
login = draugiem_complete_login(request, app, request.GET["dr_auth_code"])
login.state = SocialLogin.unstash_state(request)
ret = complete_social_login(request, login)
except (requests.RequestException, DraugiemApiError) as e:
auth_exception = e
if not ret:
ret = render_authentication_error(
request, DraugiemProvider.id, exception=auth_exception
)
return ret
def draugiem_complete_login(request, app, code):
provider = get_adapter().get_provider(request, DraugiemProvider.id)
response = requests.get(
ACCESS_TOKEN_URL,
{"action": "authorize", "app": app.secret, "code": code},
)
response.raise_for_status()
response_json = response.json()
if "error" in response_json:
raise DraugiemApiError(response_json["error"])
token = SocialToken(app=app, token=response_json["apikey"])
login = provider.sociallogin_from_response(request, response_json)
login.token = token
return login