2020-01-21 06:35:58 +00:00
|
|
|
import logging
|
|
|
|
import re
|
|
|
|
from operator import itemgetter
|
|
|
|
from urllib.parse import quote
|
|
|
|
|
|
|
|
from django import forms
|
|
|
|
from django.contrib.auth.models import User
|
|
|
|
from django.db import transaction
|
|
|
|
from django.http import HttpResponseRedirect
|
|
|
|
from django.shortcuts import render
|
|
|
|
from django.urls import reverse
|
2024-01-31 01:47:06 +00:00
|
|
|
from django.utils.translation import gettext as _
|
2020-01-21 06:35:58 +00:00
|
|
|
from requests import HTTPError
|
|
|
|
from reversion import revisions
|
|
|
|
from social_core.backends.github import GithubOAuth2
|
|
|
|
from social_core.exceptions import InvalidEmail, SocialAuthBaseException
|
|
|
|
from social_core.pipeline.partial import partial
|
2022-05-14 17:57:27 +00:00
|
|
|
from social_django.middleware import (
|
|
|
|
SocialAuthExceptionMiddleware as OldSocialAuthExceptionMiddleware,
|
|
|
|
)
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
from judge.forms import ProfileForm
|
|
|
|
from judge.models import Language, Profile
|
|
|
|
|
2022-05-14 17:57:27 +00:00
|
|
|
logger = logging.getLogger("judge.social_auth")
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
class GitHubSecureEmailOAuth2(GithubOAuth2):
|
2022-05-14 17:57:27 +00:00
|
|
|
name = "github-secure"
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
def user_data(self, access_token, *args, **kwargs):
|
|
|
|
data = self._user_data(access_token)
|
|
|
|
try:
|
2022-05-14 17:57:27 +00:00
|
|
|
emails = self._user_data(access_token, "/emails")
|
2020-01-21 06:35:58 +00:00
|
|
|
except (HTTPError, ValueError, TypeError):
|
|
|
|
emails = []
|
|
|
|
|
2022-05-14 17:57:27 +00:00
|
|
|
emails = [
|
|
|
|
(e.get("email"), e.get("primary"), 0)
|
|
|
|
for e in emails
|
|
|
|
if isinstance(e, dict) and e.get("verified")
|
|
|
|
]
|
2020-01-21 06:35:58 +00:00
|
|
|
emails.sort(key=itemgetter(1), reverse=True)
|
|
|
|
emails = list(map(itemgetter(0), emails))
|
|
|
|
|
|
|
|
if emails:
|
2022-05-14 17:57:27 +00:00
|
|
|
data["email"] = emails[0]
|
2020-01-21 06:35:58 +00:00
|
|
|
else:
|
2022-05-14 17:57:27 +00:00
|
|
|
data["email"] = None
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
return data
|
|
|
|
|
|
|
|
|
2022-05-14 17:57:27 +00:00
|
|
|
def slugify_username(username, renotword=re.compile(r"[^\w]")):
|
|
|
|
return renotword.sub("", username.replace("-", "_"))
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
def verify_email(backend, details, *args, **kwargs):
|
2022-05-14 17:57:27 +00:00
|
|
|
if not details["email"]:
|
2020-01-21 06:35:58 +00:00
|
|
|
raise InvalidEmail(backend)
|
|
|
|
|
|
|
|
|
|
|
|
class UsernameForm(forms.Form):
|
2022-05-14 17:57:27 +00:00
|
|
|
username = forms.RegexField(
|
|
|
|
regex=r"^\w+$",
|
|
|
|
max_length=30,
|
|
|
|
label="Username",
|
|
|
|
error_messages={
|
2024-01-31 01:47:06 +00:00
|
|
|
"invalid": _("A username must contain letters, numbers, or underscores")
|
2022-05-14 17:57:27 +00:00
|
|
|
},
|
|
|
|
)
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
def clean_username(self):
|
2022-05-14 17:57:27 +00:00
|
|
|
if User.objects.filter(username=self.cleaned_data["username"]).exists():
|
2024-01-31 01:47:06 +00:00
|
|
|
raise forms.ValidationError(_("Sorry, the username is taken."))
|
2022-05-14 17:57:27 +00:00
|
|
|
return self.cleaned_data["username"]
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
@partial
|
|
|
|
def choose_username(backend, user, username=None, *args, **kwargs):
|
|
|
|
if not user:
|
|
|
|
request = backend.strategy.request
|
|
|
|
if request.POST:
|
|
|
|
form = UsernameForm(request.POST)
|
|
|
|
if form.is_valid():
|
2022-05-14 17:57:27 +00:00
|
|
|
return {"username": form.cleaned_data["username"]}
|
2020-01-21 06:35:58 +00:00
|
|
|
else:
|
2022-05-14 17:57:27 +00:00
|
|
|
form = UsernameForm(initial={"username": username})
|
|
|
|
return render(
|
|
|
|
request,
|
|
|
|
"registration/username_select.html",
|
|
|
|
{
|
2024-01-31 01:47:06 +00:00
|
|
|
"title": _("Choose a username"),
|
2022-05-14 17:57:27 +00:00
|
|
|
"form": form,
|
|
|
|
},
|
|
|
|
)
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
@partial
|
|
|
|
def make_profile(backend, user, response, is_new=False, *args, **kwargs):
|
|
|
|
if is_new:
|
2022-05-14 17:57:27 +00:00
|
|
|
if not hasattr(user, "profile"):
|
2020-01-21 06:35:58 +00:00
|
|
|
profile = Profile(user=user)
|
2021-07-08 21:03:51 +00:00
|
|
|
profile.language = Language.get_default_language()
|
2022-05-14 17:57:27 +00:00
|
|
|
logger.info("Info from %s: %s", backend.name, response)
|
2020-01-21 06:35:58 +00:00
|
|
|
profile.save()
|
|
|
|
form = ProfileForm(instance=profile, user=user)
|
|
|
|
else:
|
|
|
|
data = backend.strategy.request_data()
|
|
|
|
logger.info(data)
|
|
|
|
form = ProfileForm(data, instance=user.profile, user=user)
|
|
|
|
if form.is_valid():
|
|
|
|
with transaction.atomic(), revisions.create_revision():
|
|
|
|
form.save()
|
|
|
|
revisions.set_user(user)
|
2022-05-14 17:57:27 +00:00
|
|
|
revisions.set_comment("Updated on registration")
|
2020-01-21 06:35:58 +00:00
|
|
|
return
|
2022-05-14 17:57:27 +00:00
|
|
|
return render(
|
|
|
|
backend.strategy.request,
|
|
|
|
"registration/profile_creation.html",
|
|
|
|
{
|
2024-01-31 01:47:06 +00:00
|
|
|
"title": _("Create your profile"),
|
2022-05-14 17:57:27 +00:00
|
|
|
"form": form,
|
|
|
|
},
|
|
|
|
)
|
2020-01-21 06:35:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
class SocialAuthExceptionMiddleware(OldSocialAuthExceptionMiddleware):
|
|
|
|
def process_exception(self, request, exception):
|
|
|
|
if isinstance(exception, SocialAuthBaseException):
|
2022-05-14 17:57:27 +00:00
|
|
|
return HttpResponseRedirect(
|
|
|
|
"%s?message=%s"
|
|
|
|
% (
|
|
|
|
reverse("social_auth_error"),
|
|
|
|
quote(self.get_message(request, exception)),
|
|
|
|
)
|
|
|
|
)
|