Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UI page for social login #483

Open
wants to merge 23 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/pretix/control/templates/pretixcontrol/auth/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@
</a>
{% endif %}
{% endif %}
{% if login_providers %}
{% for provider, enable in login_providers.items %}
{% if enable %}
<a href='{% url "plugins:socialauth:social.oauth.login" provider %}' method="post" class="btn btn-primary btn-block">
{% trans "Login with " %} {{ provider|capfirst }}
</a>
{% endif %}
{% endfor %}
{% endif %}
</div>
</form>
{% endblock %}
1 change: 0 additions & 1 deletion src/pretix/control/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,6 @@
url(r'^vouchers/(?P<voucher>\d+)/$', admin.VoucherUpdate.as_view(), name='admin.voucher'),
url(r'^vouchers/(?P<voucher>\d+)/delete$', admin.VoucherDelete.as_view(),
name='admin.voucher.delete'),

url(r'^global/sso/$', global_settings.SSOView.as_view(), name='admin.global.sso'),
url(r'^global/sso/(?P<pk>\d+)/delete/$', global_settings.DeleteOAuthApplicationView.as_view(), name='admin.global.sso.delete'),
url(r'^pages/$', pages.PageList.as_view(), name="admin.pages"),
Expand Down
4 changes: 4 additions & 0 deletions src/pretix/control/views/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from pretix.base.models import TeamInvite, U2FDevice, User, WebAuthnDevice
from pretix.base.models.page import Page
from pretix.base.services.mail import SendMailException
from pretix.base.settings import GlobalSettingsObject
from pretix.helpers.cookies import set_cookie_without_samesite
from pretix.helpers.jwt_generate import generate_sso_token
from pretix.multidomain.middlewares import get_cookie_domain
Expand Down Expand Up @@ -130,6 +131,9 @@ def login(request):
ctx['can_reset'] = settings.PRETIX_PASSWORD_RESET
ctx['backends'] = backends
ctx['backend'] = backend

gs = GlobalSettingsObject()
ctx['login_providers'] = gs.settings.get('login_providers', as_type=dict)
return render(request, 'pretixcontrol/auth/login.html', ctx)


Expand Down
5 changes: 5 additions & 0 deletions src/pretix/plugins/socialauth/adapter.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import logging

from allauth.core.exceptions import ImmediateHttpResponse
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from django.http import HttpResponseRedirect
from django.urls import reverse

logger = logging.getLogger(__name__)


class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
def on_authentication_error(
self, request, provider, error=None, exception=None, extra_context=None
):
logger.error('Error while authorizing with %s: %s - %s', provider, error, exception)
raise ImmediateHttpResponse(HttpResponseRedirect(reverse("control:index")))
6 changes: 5 additions & 1 deletion src/pretix/plugins/socialauth/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class PretixPluginMeta:
version = version
featured = True
description = _("This plugin allows you to login via social networks")
visible = False

def ready(self):
from . import signals # NOQA

default_app_config = 'pretix.plugins.socialauth.PaypalApp'

default_app_config = 'pretix.plugins.socialauth.SocialAuthApp'
62 changes: 0 additions & 62 deletions src/pretix/plugins/socialauth/backends.py

This file was deleted.

18 changes: 18 additions & 0 deletions src/pretix/plugins/socialauth/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from django.dispatch import receiver
from django.urls import resolve, reverse
from django.utils.translation import gettext_lazy as _

from pretix.control.signals import nav_global


@receiver(nav_global, dispatch_uid='social_uid')
def navbar_global(sender, request, **kwargs):
url = resolve(request.path_info)
return [
{
'parent': reverse('control:admin.global.settings'),
'label': _('Social login settings'),
'url': reverse('plugins:socialauth:admin.global.social.auth.settings'),
'active': (url.url_name == 'admin.global.social.auth.settings')
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{% extends "pretixcontrol/admin/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load rich_text %}

{% block title %}{% trans "Social login settings" %}{% endblock %}
{% block content %}
<nav id="event-nav" class="header-nav">
<div class="navigation">
<div class="navigation-title">
<h1>{% trans "Social login settings" %}</h1>
</div>
{% include "pretixcontrol/event/component_link.html" %}
</div>
</nav>
<form action="" method="post" class="form-horizontal form-plugins">
{% csrf_token %}
{% if "success" in request.GET %}
<div class="alert alert-success">
{% trans "Your changes have been saved." %}
</div>
{% endif %}
<div class="tabbed-form">
<fieldset>
<legend>{% trans "Social login providers" %}</legend>
<div class="table-responsive">
<table class="table">
{% for provider, enable in login_providers.items %}
<tr class="{% if enable %}success{% else %}default{% endif %}">
<td>
<strong>{% trans provider %}</strong>
<p>{% trans "Login with " %}{{ provider|capfirst }}</p>
</td>
<td class="text-right flip" width="20%">
HungNgien marked this conversation as resolved.
Show resolved Hide resolved
{% if enable %}
<button class="btn btn-default btn-block" name="{{ provider }}_login" value="disable">
{% trans "Disable" %}
</button>
{% else %}
<button class="btn btn-default btn-block" name="{{ provider }}_login" value="enable">
{% trans "Enable" %}
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
</fieldset>
</div>
</form>
{% endblock %}
4 changes: 3 additions & 1 deletion src/pretix/plugins/socialauth/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
from . import views

urlpatterns = [
url(r'^oauth_return$', views.oauth_return, name='social.oauth.return')
url(r'^oauth_login/(?P<provider>[a-zA-Z]+)/$', views.oauth_login, name='social.oauth.login'),
url(r'^oauth_return$', views.oauth_return, name='social.oauth.return'),
url(r'^control/global/social_auth/$', views.SocialLoginView.as_view(), name='admin.global.social.auth.settings'),
]
52 changes: 52 additions & 0 deletions src/pretix/plugins/socialauth/views.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
import logging
from urllib.parse import urlencode, urlparse, urlunparse

from allauth.socialaccount.adapter import get_adapter
from django.conf import settings
from django.contrib import messages
from django.shortcuts import redirect
from django.urls import reverse
from django.views.generic import TemplateView

from pretix.base.models import User
from pretix.base.settings import GlobalSettingsObject
from pretix.control.permissions import AdministratorPermissionRequiredMixin
from pretix.control.views.auth import process_login_and_set_cookie
from pretix.helpers.urls import build_absolute_uri

logger = logging.getLogger(__name__)
adapter = get_adapter()


def oauth_login(request, provider):
base_url = adapter.get_provider(request, provider).get_login_url(request)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Preserve existing query parameters when constructing OAuth login URL

The current implementation overwrites any existing query parameters in base_url. Should merge new parameters with existing ones to avoid breaking providers that require specific parameters.

query_params = {
"next": build_absolute_uri("plugins:socialauth:social.oauth.return")
}
parsed_url = urlparse(base_url)
updated_url = parsed_url._replace(query=urlencode(query_params))
return redirect(urlunparse(updated_url))


def oauth_return(request):
Expand All @@ -28,3 +46,37 @@ def oauth_return(request):
)
logger.error('Error while authorizing: user has no email address.')
return redirect('control:auth.login')


class SocialLoginView(AdministratorPermissionRequiredMixin, TemplateView):
template_name = 'socialauth/social_auth_settings.html'
LOGIN_PROVIDERS = {'mediawiki': False, 'github': False, 'google': False}
VALID_STATES = {'enable', 'disable'}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.gs = GlobalSettingsObject()

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.gs.settings.get('login_providers', as_type=dict) is None:
self.gs.settings.set('login_providers', self.LOGIN_PROVIDERS)
context['login_providers'] = self.gs.settings.get('login_providers', as_type=dict)
return context

def post(self, request, *args, **kwargs):
login_providers = self.gs.settings.get('login_providers', as_type=dict)
for provider in self.LOGIN_PROVIDERS.keys():
value = request.POST.get(f'{provider}_login', '').lower()
if value not in self.VALID_STATES:
continue
elif value == "enable":
HungNgien marked this conversation as resolved.
Show resolved Hide resolved
login_providers[provider] = True
elif value == "disable":
login_providers[provider] = False
self.gs.settings.set('login_providers', login_providers)
break
return redirect(self.get_success_url())

def get_success_url(self) -> str:
return reverse('plugins:socialauth:admin.global.social.auth.settings')
Loading