diff --git a/ccvpn/settings.py b/ccvpn/settings.py index f499cba..e5b4dbd 100644 --- a/ccvpn/settings.py +++ b/ccvpn/settings.py @@ -155,6 +155,10 @@ SECURE_SSL_REDIRECT = False SECURE_HSTS_SECONDS = 3600 SECURE_HSTS_INCLUDE_SUBDOMAINS = True +# Enable Discourse SSO +DISCOURSE_SSO = False +DISCOURSE_SECRET = '...' +DISCOURSE_URL = 'https://forum.ccrypto.org/' # OpenVPN CA Certificate with open(BASE_DIR + '/ccvpn/ca.crt') as ca_file: diff --git a/lambdainst/forms.py b/lambdainst/forms.py index f552dc0..e2aae14 100644 --- a/lambdainst/forms.py +++ b/lambdainst/forms.py @@ -54,3 +54,11 @@ class SignupForm(forms.Form, FormPureRender): raise forms.ValidationError(_("Passwords are not the same")) return self.data['password'] + +class ReqEmailForm(forms.Form, FormPureRender): + email = forms.EmailField( + label=_("E-Mail"), + widget=forms.EmailInput(attrs={'placeholder': _("E-Mail")}), + ) + + diff --git a/lambdainst/urls.py b/lambdainst/urls.py index 5d1a149..8979821 100644 --- a/lambdainst/urls.py +++ b/lambdainst/urls.py @@ -5,6 +5,7 @@ from . import views urlpatterns = [ url(r'^login$', auth_views.login, name='login'), + url(r'^discourse_login', views.discourse_login, name='discourse_login'), url(r'^logout$', views.logout, name='logout'), url(r'^signup$', views.signup, name='signup'), diff --git a/lambdainst/views.py b/lambdainst/views.py index 67e5463..59a8681 100644 --- a/lambdainst/views.py +++ b/lambdainst/views.py @@ -1,11 +1,17 @@ import requests import io import zipfile -from urllib.parse import urlencode +import hmac +import base64 +from hashlib import sha256 +from urllib.parse import urlencode, parse_qsl from datetime import timedelta, datetime -from django.http import HttpResponse, HttpResponseNotFound, HttpResponseForbidden -from django.http import JsonResponse +from django.http import ( + HttpResponse, JsonResponse, + HttpResponseRedirect, + HttpResponseNotFound, HttpResponseForbidden +) from django.shortcuts import render, redirect from django.contrib.auth import authenticate from django.contrib.auth.decorators import login_required, user_passes_test @@ -21,7 +27,7 @@ from django.contrib.auth.models import User from django_countries import countries from payments.models import ACTIVE_BACKENDS -from .forms import SignupForm +from .forms import SignupForm, ReqEmailForm from .models import GiftCode, VPNUser from .core import core_api from . import core @@ -85,6 +91,58 @@ def signup(request): return redirect('account:index') +@login_required +def discourse_login(request): + sso_secret = project_settings.DISCOURSE_SECRET + discourse_url = project_settings.DISCOURSE_URL + + if project_settings.DISCOURSE_SSO is not True: + return HttpResponseNotFound() + + payload = request.GET.get('sso', '') + signature = request.GET.get('sig', '') + + expected_signature = hmac.new(sso_secret.encode('utf-8'), + payload.encode('utf-8'), + sha256).hexdigest() + + if signature != expected_signature: + return HttpResponseNotFound() + + if request.method == 'POST' and 'email' in request.POST: + form = ReqEmailForm(request.POST) + if not form.is_valid(): + return render(request, 'ccvpn/require_email.html', dict(form=form)) + + request.user.email = form.cleaned_data['email'] + request.user.save() + + if not request.user.email: + form = ReqEmailForm() + return render(request, 'ccvpn/require_email.html', dict(form=form)) + + try: + payload = base64.b64decode(payload).decode('utf-8') + payload_data = dict(parse_qsl(payload)) + except (TypeError, ValueError): + return HttpResponseNotFound() + + payload_data.update({ + 'external_id': request.user.id, + 'username': request.user.username, + 'email': request.user.email, + 'require_activation': 'true', + }) + + payload = urlencode(payload_data) + payload = base64.b64encode(payload.encode('utf-8')) + signature = hmac.new(sso_secret.encode('utf-8'), payload, sha256).hexdigest() + redirect_query = urlencode(dict(sso=payload, sig=signature)) + redirect_path = '/session/sso_login?' + redirect_query + + return HttpResponseRedirect(discourse_url + redirect_path) + + @login_required def index(request): ref_url = project_settings.ROOT_URL + '?ref=' + str(request.user.id) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 80c7d88..463516b 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-07-07 17:43+0000\n" +"POT-Creation-Date: 2016-09-01 03:18+0000\n" "PO-Revision-Date: 2016-04-07 01:32+0000\n" "Last-Translator: \n" "Language-Team: \n" @@ -27,39 +27,39 @@ msgstr "" msgid "hash" msgstr "" -#: lambdainst/admin.py:17 +#: lambdainst/admin.py:22 msgid "Code must be [a-zA-Z0-9]" msgstr "" -#: lambdainst/admin.py:19 +#: lambdainst/admin.py:24 msgid "Code must be between 1 and 32 characters" msgstr "" -#: lambdainst/admin.py:39 +#: lambdainst/admin.py:43 msgid "(rewarded)" msgstr "" -#: lambdainst/admin.py:41 +#: lambdainst/admin.py:45 msgid "(not rewarded)" msgstr "" -#: lambdainst/admin.py:43 +#: lambdainst/admin.py:48 msgid "Referrer" msgstr "" -#: lambdainst/admin.py:48 lambdainst/admin.py:92 +#: lambdainst/admin.py:53 lambdainst/admin.py:96 msgid "Is paid?" msgstr "Est payé?" -#: lambdainst/admin.py:83 +#: lambdainst/admin.py:87 msgid "Important dates" msgstr "Dates importantes" -#: lambdainst/admin.py:84 +#: lambdainst/admin.py:88 msgid "Permissions" msgstr "Permissions" -#: lambdainst/admin.py:116 tickets/admin.py:52 +#: lambdainst/admin.py:133 tickets/admin.py:52 msgid "Comment" msgstr "Notes" @@ -87,8 +87,9 @@ msgstr "Répétez" msgid "Same Anything" msgstr "La même chose" -#: lambdainst/forms.py:47 lambdainst/forms.py:48 templates/ccvpn/signup.html:30 -#: templates/lambdainst/settings.html.py:22 +#: lambdainst/forms.py:47 lambdainst/forms.py:48 lambdainst/forms.py:60 +#: lambdainst/forms.py:61 templates/ccvpn/require_email.html.py:13 +#: templates/ccvpn/signup.html:30 templates/lambdainst/settings.html.py:22 #: templates/registration/password_reset_form.html:11 msgid "E-Mail" msgstr "E-Mail" @@ -97,27 +98,27 @@ msgstr "E-Mail" msgid "Passwords are not the same" msgstr "Les mots de passe de correspondent pas" -#: lambdainst/models.py:24 +#: lambdainst/models.py:25 msgid "VPN User" msgstr "VPN User" -#: lambdainst/models.py:25 +#: lambdainst/models.py:26 msgid "VPN Users" msgstr "VPN Users" -#: lambdainst/models.py:92 +#: lambdainst/models.py:97 msgid "Gift Code" msgstr "Code cadeau" -#: lambdainst/models.py:93 +#: lambdainst/models.py:98 msgid "Gift Codes" msgstr "Codes cadeau" -#: lambdainst/models.py:138 +#: lambdainst/models.py:143 msgid "Gift Code User" msgstr "Utilisateur de codes" -#: lambdainst/models.py:139 +#: lambdainst/models.py:144 msgid "Gift Code Users" msgstr "Utilisateurs de codes" @@ -200,50 +201,50 @@ msgstr "" msgid "%s Pbps" msgstr "" -#: lambdainst/views.py:91 +#: lambdainst/views.py:152 msgid "Awesome VPN! 3€ per month, with a free 7 days trial!" msgstr "" -#: lambdainst/views.py:98 templates/account_layout.html.py:9 +#: lambdainst/views.py:159 templates/account_layout.html.py:9 #: templates/account_layout.html:11 templates/lambdainst/account.html.py:10 msgid "Account" msgstr "Compte" -#: lambdainst/views.py:137 lambdainst/views.py:180 +#: lambdainst/views.py:198 lambdainst/views.py:221 lambdainst/views.py:246 msgid "OK!" msgstr "OK!" -#: lambdainst/views.py:139 +#: lambdainst/views.py:200 msgid "Invalid captcha" msgstr "Captcha invalide" -#: lambdainst/views.py:153 +#: lambdainst/views.py:214 msgid "Passwords do not match" msgstr "Les mots de passe ne correspondent pas" -#: lambdainst/views.py:165 templates/account_layout.html.py:13 +#: lambdainst/views.py:231 templates/account_layout.html.py:13 #: templates/lambdainst/settings.html:6 msgid "Settings" msgstr "Options" -#: lambdainst/views.py:176 +#: lambdainst/views.py:242 msgid "Gift code not found or already used." msgstr "Code inconnu ou déjà utilisé." -#: lambdainst/views.py:178 +#: lambdainst/views.py:244 msgid "Gift code only available to free accounts." msgstr "Code uniquement disponible pour les nouveaux comptes." -#: lambdainst/views.py:200 templates/account_layout.html.py:15 +#: lambdainst/views.py:266 templates/account_layout.html.py:15 #: templates/lambdainst/logs.html:6 msgid "Logs" msgstr "Logs" -#: lambdainst/views.py:207 templates/lambdainst/config.html.py:7 +#: lambdainst/views.py:273 templates/lambdainst/config.html.py:7 msgid "Config" msgstr "Config" -#: lambdainst/views.py:296 payments/backends.py:120 payments/backends.py:122 +#: lambdainst/views.py:377 payments/backends.py:120 payments/backends.py:122 #: templates/admin/index.html:53 templates/admin/index.html.py:56 #: templates/lambdainst/admin_ref.html:16 #: templates/lambdainst/admin_status.html:16 templates/payments/list.html:13 @@ -555,6 +556,18 @@ msgstr "Installation" msgid "GNU/Linux" msgstr "GNU/Linux" +#: templates/ccvpn/require_email.html:8 +msgid "E-mail Confirmation" +msgstr "Confirmation de l'adresse e-mail" + +#: templates/ccvpn/require_email.html:16 +msgid "Required to use the forums. A confirmation will be sent." +msgstr "Nécessaire pour les forums. Une confirmation sera envoyée." + +#: templates/ccvpn/require_email.html:19 +msgid "Continue" +msgstr "Continuer" + #: templates/ccvpn/signup.html:8 templates/ccvpn/signup.html.py:37 #: templates/layout.html:34 msgid "Sign up" diff --git a/templates/ccvpn/require_email.html b/templates/ccvpn/require_email.html new file mode 100644 index 0000000..df6f2c1 --- /dev/null +++ b/templates/ccvpn/require_email.html @@ -0,0 +1,25 @@ +{% extends 'layout.html' %} +{% load i18n %} +{% load staticfiles %} + +{% block content %} +
+
+

{% trans 'E-mail Confirmation' %}

+
+ {% csrf_token %} + + {{ form.email.errors }} + + +

+ {% trans 'Required to use the forums. A confirmation will be sent.' %} +

+ + +
+
+
+ +{% endblock %} +