204 lines
6.9 KiB
Python
204 lines
6.9 KiB
Python
from django.contrib import messages
|
|
from django.forms import ValidationError
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import render
|
|
from django.urls import reverse
|
|
|
|
from allauth.account import app_settings as account_settings
|
|
from allauth.account.adapter import get_adapter as get_account_adapter
|
|
from allauth.account.utils import (
|
|
complete_signup,
|
|
perform_login,
|
|
user_display,
|
|
user_username,
|
|
)
|
|
from allauth.exceptions import ImmediateHttpResponse
|
|
|
|
from . import app_settings, signals
|
|
from .adapter import get_adapter
|
|
from .models import SocialLogin
|
|
from .providers.base import AuthError, AuthProcess
|
|
|
|
|
|
def _process_signup(request, sociallogin):
|
|
auto_signup = get_adapter(request).is_auto_signup_allowed(request, sociallogin)
|
|
if not auto_signup:
|
|
request.session["socialaccount_sociallogin"] = sociallogin.serialize()
|
|
url = reverse("socialaccount_signup")
|
|
ret = HttpResponseRedirect(url)
|
|
else:
|
|
# Ok, auto signup it is, at least the e-mail address is ok.
|
|
# We still need to check the username though...
|
|
if account_settings.USER_MODEL_USERNAME_FIELD:
|
|
username = user_username(sociallogin.user)
|
|
try:
|
|
get_account_adapter(request).clean_username(username)
|
|
except ValidationError:
|
|
# This username is no good ...
|
|
user_username(sociallogin.user, "")
|
|
# FIXME: This part contains a lot of duplication of logic
|
|
# ("closed" rendering, create user, send email, in active
|
|
# etc..)
|
|
if not get_adapter(request).is_open_for_signup(request, sociallogin):
|
|
return render(
|
|
request,
|
|
"account/signup_closed." + account_settings.TEMPLATE_EXTENSION,
|
|
)
|
|
get_adapter(request).save_user(request, sociallogin, form=None)
|
|
ret = complete_social_signup(request, sociallogin)
|
|
return ret
|
|
|
|
|
|
def _login_social_account(request, sociallogin):
|
|
return perform_login(
|
|
request,
|
|
sociallogin.user,
|
|
email_verification=app_settings.EMAIL_VERIFICATION,
|
|
redirect_url=sociallogin.get_redirect_url(request),
|
|
signal_kwargs={"sociallogin": sociallogin},
|
|
)
|
|
|
|
|
|
def render_authentication_error(
|
|
request,
|
|
provider_id,
|
|
error=AuthError.UNKNOWN,
|
|
exception=None,
|
|
extra_context=None,
|
|
):
|
|
try:
|
|
if extra_context is None:
|
|
extra_context = {}
|
|
get_adapter(request).authentication_error(
|
|
request,
|
|
provider_id,
|
|
error=error,
|
|
exception=exception,
|
|
extra_context=extra_context,
|
|
)
|
|
except ImmediateHttpResponse as e:
|
|
return e.response
|
|
if error == AuthError.CANCELLED:
|
|
return HttpResponseRedirect(reverse("socialaccount_login_cancelled"))
|
|
context = {
|
|
"auth_error": {
|
|
"provider": provider_id,
|
|
"code": error,
|
|
"exception": exception,
|
|
}
|
|
}
|
|
context.update(extra_context)
|
|
return render(
|
|
request,
|
|
"socialaccount/authentication_error." + account_settings.TEMPLATE_EXTENSION,
|
|
context,
|
|
)
|
|
|
|
|
|
def _add_social_account(request, sociallogin):
|
|
if request.user.is_anonymous:
|
|
# This should not happen. Simply redirect to the connections
|
|
# view (which has a login required)
|
|
return HttpResponseRedirect(reverse("socialaccount_connections"))
|
|
level = messages.INFO
|
|
message = "socialaccount/messages/account_connected.txt"
|
|
action = None
|
|
if sociallogin.is_existing:
|
|
if sociallogin.user != request.user:
|
|
# Social account of other user. For now, this scenario
|
|
# is not supported. Issue is that one cannot simply
|
|
# remove the social account from the other user, as
|
|
# that may render the account unusable.
|
|
level = messages.ERROR
|
|
message = "socialaccount/messages/account_connected_other.txt"
|
|
else:
|
|
# This account is already connected -- we give the opportunity
|
|
# for customized behaviour through use of a signal.
|
|
action = "updated"
|
|
message = "socialaccount/messages/account_connected_updated.txt"
|
|
signals.social_account_updated.send(
|
|
sender=SocialLogin, request=request, sociallogin=sociallogin
|
|
)
|
|
else:
|
|
# New account, let's connect
|
|
action = "added"
|
|
sociallogin.connect(request, request.user)
|
|
signals.social_account_added.send(
|
|
sender=SocialLogin, request=request, sociallogin=sociallogin
|
|
)
|
|
default_next = get_adapter(request).get_connect_redirect_url(
|
|
request, sociallogin.account
|
|
)
|
|
next_url = sociallogin.get_redirect_url(request) or default_next
|
|
get_account_adapter(request).add_message(
|
|
request,
|
|
level,
|
|
message,
|
|
message_context={"sociallogin": sociallogin, "action": action},
|
|
)
|
|
return HttpResponseRedirect(next_url)
|
|
|
|
|
|
def complete_social_login(request, sociallogin):
|
|
assert not sociallogin.is_existing
|
|
sociallogin.lookup()
|
|
try:
|
|
get_adapter(request).pre_social_login(request, sociallogin)
|
|
signals.pre_social_login.send(
|
|
sender=SocialLogin, request=request, sociallogin=sociallogin
|
|
)
|
|
process = sociallogin.state.get("process")
|
|
if process == AuthProcess.REDIRECT:
|
|
return _social_login_redirect(request, sociallogin)
|
|
elif process == AuthProcess.CONNECT:
|
|
return _add_social_account(request, sociallogin)
|
|
else:
|
|
return _complete_social_login(request, sociallogin)
|
|
except ImmediateHttpResponse as e:
|
|
return e.response
|
|
|
|
|
|
def _social_login_redirect(request, sociallogin):
|
|
next_url = sociallogin.get_redirect_url(request) or "/"
|
|
return HttpResponseRedirect(next_url)
|
|
|
|
|
|
def _complete_social_login(request, sociallogin):
|
|
if request.user.is_authenticated:
|
|
get_account_adapter(request).logout(request)
|
|
if sociallogin.is_existing:
|
|
# Login existing user
|
|
ret = _login_social_account(request, sociallogin)
|
|
signals.social_account_updated.send(
|
|
sender=SocialLogin, request=request, sociallogin=sociallogin
|
|
)
|
|
else:
|
|
# New social user
|
|
ret = _process_signup(request, sociallogin)
|
|
return ret
|
|
|
|
|
|
def complete_social_signup(request, sociallogin):
|
|
return complete_signup(
|
|
request,
|
|
sociallogin.user,
|
|
app_settings.EMAIL_VERIFICATION,
|
|
sociallogin.get_redirect_url(request),
|
|
signal_kwargs={"sociallogin": sociallogin},
|
|
)
|
|
|
|
|
|
# TODO: Factor out callable importing functionality
|
|
# See: account.utils.user_display
|
|
def import_path(path):
|
|
modname, _, attr = path.rpartition(".")
|
|
m = __import__(modname, fromlist=[attr])
|
|
return getattr(m, attr)
|
|
|
|
|
|
def socialaccount_user_display(socialaccount):
|
|
func = app_settings.SOCIALACCOUNT_STR
|
|
if not func:
|
|
return user_display(socialaccount.user)
|
|
return func(socialaccount)
|