From 4a0ddb3b1725b56ca66fa991ec8c8d6c6eac113f Mon Sep 17 00:00:00 2001 From: Helgi Hrafn Gunnarsson Date: Sat, 19 May 2018 17:14:13 +0000 Subject: [PATCH 1/7] Minor: Unused file core/utils.py removed. --- core/utils.py | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 core/utils.py diff --git a/core/utils.py b/core/utils.py deleted file mode 100644 index 8625351f..00000000 --- a/core/utils.py +++ /dev/null @@ -1,22 +0,0 @@ -from HTMLParser import HTMLParser - -#class MLStripper(HTMLParser): -# def __init__(self): -# self.reset() -# self.fed = [] -# def handle_data(self, d): -# self.fed.append(d) -# def get_data(self): -# return ''.join(self.fed) -# -#def strip_tags(html): -# s = MLStripper() -# s.feed(html) -# return s.get_data() - - -class AttrDict(dict): - __getattr__ = dict.__getitem__ - - def __setattr__(self, key, value): - self[key] = value From 2eb6d7a5a2cde20a9f9e24d02663aa207d27cee9 Mon Sep 17 00:00:00 2001 From: Helgi Hrafn Gunnarsson Date: Sat, 19 May 2018 21:25:58 +0000 Subject: [PATCH 2/7] GDPR-compliance: Terms and conditions implemented. (Need setting in admin interface to actually do anything.) --- core/urls.py | 2 + requirements.txt | 1 + wasa2il/settings.py | 10 ++++ .../termsandconditions/tc_accept_terms.html | 52 +++++++++++++++++++ 4 files changed, 65 insertions(+) create mode 100644 wasa2il/templates/termsandconditions/tc_accept_terms.html diff --git a/core/urls.py b/core/urls.py index f646e450..fcd3a13c 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,3 +1,4 @@ +from django.conf.urls import include from django.conf.urls import url from core import views as core_views @@ -5,4 +6,5 @@ urlpatterns = [ url(r'^$', core_views.home), + url(r'^terms/', include('termsandconditions.urls')), ] diff --git a/requirements.txt b/requirements.txt index cdcaf2f5..7dbbeb25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,6 +20,7 @@ django-datetime-widget==0.9.3 django-debug-toolbar==1.9.1 django-prosemirror==0.0.12 django-registration-redux==1.10 +django-termsandconditions==1.2.8 eight==0.4.2 enum34==1.1.6 et-xmlfile==1.0.1 diff --git a/wasa2il/settings.py b/wasa2il/settings.py index d96e2187..a0642c52 100644 --- a/wasa2il/settings.py +++ b/wasa2il/settings.py @@ -114,6 +114,7 @@ 'core.middleware.UserSettingsMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', 'core.middleware.GlobalsMiddleware', + 'termsandconditions.middleware.TermsAndConditionsRedirectMiddleware', ) try: MIDDLEWARE_CLASSES += LOCAL_MIDDLEWARE_CLASSES @@ -165,6 +166,7 @@ 'datetimewidget', 'crispy_forms', 'prosemirror', + 'termsandconditions', 'core', 'polity', @@ -225,6 +227,14 @@ } } +TERMS_EXCLUDE_URL_PREFIX_LIST = ( + '/admin/', + '/help/', + '/accounts/register/', + '/accounts/login/', + '/accounts/logout/', +) + AUTH_PROFILE_MODULE = "core.UserProfile" ACCOUNT_ACTIVATION_DAYS = 7 LOGIN_REDIRECT_URL = "/" diff --git a/wasa2il/templates/termsandconditions/tc_accept_terms.html b/wasa2il/templates/termsandconditions/tc_accept_terms.html new file mode 100644 index 00000000..66cf4a14 --- /dev/null +++ b/wasa2il/templates/termsandconditions/tc_accept_terms.html @@ -0,0 +1,52 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load i18n %} +{% load wasa2il %} + +{% block styles %} + {{ block.super }} + +{% endblock %} + +{% block content %} +
+ + {{ form.errors }} + + {% for terms in form.initial.terms %} + +

+ {% trans 'Terms and Conditions' %} + ({% trans 'version' %} {{ terms.version_number|safe }}) +

+ +

{% trans 'In order to use this software, you must accept the following terms and conditions.' %}

+ + {% if terms.info %} +

{% trans 'Information' %}:

+
+

{{ terms.info|markdown }}

+
+ {% endif %} + +
+ {{ terms.text|safe|markdown }} + + {% endfor %} + +
+
+ {% csrf_token %} + {{ form.terms }} + {{ form.returnTo }} + + {% trans 'Logout' %} +
+ +{% endblock %} From 6def8d860e1aabaaa569b4a46e580dd7f1bb26cc Mon Sep 17 00:00:00 2001 From: Helgi Hrafn Gunnarsson Date: Sun, 20 May 2018 18:52:57 +0000 Subject: [PATCH 3/7] Bugfix: SAML verification was broken due to malconfigured Terms and Services. --- wasa2il/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wasa2il/settings.py b/wasa2il/settings.py index a0642c52..ebbfedbb 100644 --- a/wasa2il/settings.py +++ b/wasa2il/settings.py @@ -233,6 +233,7 @@ '/accounts/register/', '/accounts/login/', '/accounts/logout/', + '/accounts/verify/', ) AUTH_PROFILE_MODULE = "core.UserProfile" From cd8af27e9bb4c9a87d3d65ca4a964a756f51129d Mon Sep 17 00:00:00 2001 From: Helgi Hrafn Gunnarsson Date: Sun, 20 May 2018 20:27:00 +0000 Subject: [PATCH 4/7] SAML verification fixes and error handling improvements. --- core/views.py | 7 ++++++- wasa2il/templates/registration/verification_duplicate.html | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/views.py b/core/views.py index 1c9e7936..e233b8a9 100644 --- a/core/views.py +++ b/core/views.py @@ -1,5 +1,6 @@ from datetime import datetime, timedelta +from xml.etree.ElementTree import ParseError import os.path import json @@ -11,6 +12,7 @@ from urlparse import parse_qs # SSO done +from django.contrib.auth import logout from django.core.urlresolvers import reverse from django.shortcuts import render from django.shortcuts import redirect, get_object_or_404 @@ -231,6 +233,9 @@ def verify(request): except SamlException as e: ctx = {'e': e} return render(request, 'registration/saml_error.html', ctx) + except ParseError: + logout(request) + return redirect(reverse('auth_login')) if UserProfile.objects.filter(verified_ssn=auth['ssn']).exists(): taken_user = UserProfile.objects.select_related('user').get(verified_ssn=auth['ssn']).user @@ -239,7 +244,7 @@ def verify(request): 'taken_user': taken_user, } - auth_logout(request) + logout(request) return render(request, 'registration/verification_duplicate.html', ctx) diff --git a/wasa2il/templates/registration/verification_duplicate.html b/wasa2il/templates/registration/verification_duplicate.html index 8b9b9f25..0d3a9583 100644 --- a/wasa2il/templates/registration/verification_duplicate.html +++ b/wasa2il/templates/registration/verification_duplicate.html @@ -11,7 +11,7 @@

{% trans "Multiple accounts detected" %}

  • {% trans "Username" %}: {{ taken_user.username }}
  • {% trans "Email" %}: {{ taken_user.email }}
  • -

    Login

    +

    {% trans 'Login' %}

    {% endblock %} From 069aef9a77ab62506113961fd3f3e554f037df3c Mon Sep 17 00:00:00 2001 From: Helgi Hrafn Gunnarsson Date: Sun, 20 May 2018 21:05:23 +0000 Subject: [PATCH 5/7] SSNs checked to see if they represent humans of a minimum age, when specified. --- core/urls.py | 2 +- core/utils.py | 40 +++++++++++++++++++ core/views.py | 22 ++++++++++ requirements.txt | 1 + wasa2il/default_settings.py | 3 ++ wasa2il/local_settings.py-example | 3 ++ .../registration/verification_age_limit.html | 23 +++++++++++ .../verification_invalid_entity.html | 24 +++++++++++ 8 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 core/utils.py create mode 100644 wasa2il/templates/registration/verification_age_limit.html create mode 100644 wasa2il/templates/registration/verification_invalid_entity.html diff --git a/core/urls.py b/core/urls.py index fcd3a13c..27e95114 100644 --- a/core/urls.py +++ b/core/urls.py @@ -5,6 +5,6 @@ urlpatterns = [ - url(r'^$', core_views.home), + url(r'^$', core_views.home, name='home'), url(r'^terms/', include('termsandconditions.urls')), ] diff --git a/core/utils.py b/core/utils.py new file mode 100644 index 00000000..308549bd --- /dev/null +++ b/core/utils.py @@ -0,0 +1,40 @@ +from datetime import datetime + +from dateutil.relativedelta import relativedelta + +def ssn_is_formatted_correctly(ssn): + # We don't need any hard-core checksumming here, since we're only making + # sure that the data format is correct, so that we can safely retrieve + # parts of it through string manipulation. + return ssn.isdigit() and len(ssn) == 10 + +def calculate_age_from_ssn(ssn): + if not ssn_is_formatted_correctly(ssn): + raise AttributeError('SSN must be numeric and exactly 10 digits long') + + # Determine year. + century_num = ssn[9:] + if century_num == '9': + century = 1900 + elif century_num == '0': + century = 2000 + else: + raise AttributeError('%s is not a known number for any century' % century_num) + year = century + int(ssn[4:6]) + + # Determine month and day + month = int(ssn[2:4]) + day = int(ssn[0:2]) + + # Calculate the differences between birthdate and today. + birthdate = datetime(year, month, day) + today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + age = relativedelta(today, birthdate).years + + return age + +def is_ssn_human_or_institution(ssn): + if not ssn_is_formatted_correctly(ssn): + raise AttributeError('SSN must be numeric and exactly 10 digits long') + + return 'institution' if int(ssn[0:2]) > 31 else 'human' diff --git a/core/views.py b/core/views.py index e233b8a9..334f16dc 100644 --- a/core/views.py +++ b/core/views.py @@ -35,6 +35,8 @@ from core.models import UserProfile from core.forms import UserProfileForm from core.saml import authenticate, SamlException +from core.utils import calculate_age_from_ssn +from core.utils import is_ssn_human_or_institution from election.models import Election from issue.forms import DocumentForm from issue.models import Document @@ -237,6 +239,26 @@ def verify(request): logout(request) return redirect(reverse('auth_login')) + # Make sure that the user is, in fact, human. + if is_ssn_human_or_institution(auth['ssn']) != 'human': + ctx = { + 'ssn': auth['ssn'], + 'name': auth['name'].encode('utf8'), + } + return render(request, 'registration/verification_invalid_entity.html', ctx) + + + # Make sure that user has reached the minimum required age, if applicable. + if hasattr(settings, 'AGE_LIMIT') and settings.AGE_LIMIT > 0: + age = calculate_age_from_ssn(auth['ssn']) + if age < settings.AGE_LIMIT: + logout(request) + ctx = { + 'age': age, + 'age_limit': settings.AGE_LIMIT, + } + return render(request, 'registration/verification_age_limit.html', ctx) + if UserProfile.objects.filter(verified_ssn=auth['ssn']).exists(): taken_user = UserProfile.objects.select_related('user').get(verified_ssn=auth['ssn']).user ctx = { diff --git a/requirements.txt b/requirements.txt index 7dbbeb25..7614fa8b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -62,6 +62,7 @@ pyflakes==1.6.0 Pygments==2.2.0 PyMySQL==0.8.1 pyOpenSSL==18.0.0 +python-dateutil==2.7.3 python-memcached==1.59 python-vote-full==1.0 pytz==2018.4 diff --git a/wasa2il/default_settings.py b/wasa2il/default_settings.py index ba77b533..981bb178 100644 --- a/wasa2il/default_settings.py +++ b/wasa2il/default_settings.py @@ -29,6 +29,9 @@ ALLOW_LEAVE_POLITY = False +# Age limit for participation. (Currently only works with SAML.) +AGE_LIMIT = 16 + DATE_FORMAT = 'd/m/Y' DATETIME_FORMAT = 'd/m/Y H:i:s' DATETIME_FORMAT_DJANGO_WIDGET = 'dd/mm/yyyy hh:ii' # django-datetime-widget diff --git a/wasa2il/local_settings.py-example b/wasa2il/local_settings.py-example index 0256fd0b..b6c88ce0 100644 --- a/wasa2il/local_settings.py-example +++ b/wasa2il/local_settings.py-example @@ -23,6 +23,9 @@ TIME_ZONE = 'Iceland' ALLOW_LEAVE_POLITY = False +# Age limit for participation. (Currently only works with SAML.) +AGE_LIMIT = 16 + DATE_FORMAT = 'd/m/Y' DATETIME_FORMAT = 'd/m/Y H:i:s' DATETIME_FORMAT_DJANGO_WIDGET = 'dd/mm/yyyy hh:ii' # django-datetime-widget diff --git a/wasa2il/templates/registration/verification_age_limit.html b/wasa2il/templates/registration/verification_age_limit.html new file mode 100644 index 00000000..ac8baff0 --- /dev/null +++ b/wasa2il/templates/registration/verification_age_limit.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} + +
    + +
    + +

    {% trans 'Age below age limit' %}

    + +

    {% blocktrans %}We are sorry to inform you that you have not yet reached the minimum required age of {{ age_limit }} years. According to our information, you are {{ age }} years old.{% endblocktrans %}

    + +

    {% trans 'We will be very happy to provide you with access once you reach the minimum required age.' %}

    + + {% trans 'Front page' %} + +
    + +
    + +{% endblock %} + diff --git a/wasa2il/templates/registration/verification_invalid_entity.html b/wasa2il/templates/registration/verification_invalid_entity.html new file mode 100644 index 00000000..0a4a49d6 --- /dev/null +++ b/wasa2il/templates/registration/verification_invalid_entity.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block content %} + +
    + +
    + +

    {% trans 'Invalid entity' %}

    + +

    {% blocktrans %}The entity {{ name }} with the SSN {{ ssn }} is an invalid entity.{% endblocktrans %}

    + +

    {% blocktrans %}Please try using a valid entity for verifying your account.{% endblocktrans %}

    + + + {% trans 'Logout' %} + +
    + +
    + +{% endblock %} + From 05543581cecdda15c3e75120de52f3ccbe381e79 Mon Sep 17 00:00:00 2001 From: Helgi Hrafn Gunnarsson Date: Sun, 20 May 2018 21:07:05 +0000 Subject: [PATCH 6/7] Translation: Icelandic --- wasa2il/locale/is/LC_MESSAGES/django.po | 79 +++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/wasa2il/locale/is/LC_MESSAGES/django.po b/wasa2il/locale/is/LC_MESSAGES/django.po index e85601dc..a33e1c56 100644 --- a/wasa2il/locale/is/LC_MESSAGES/django.po +++ b/wasa2il/locale/is/LC_MESSAGES/django.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-05-19 16:26+0000\n" +"POT-Creation-Date: 2018-05-20 21:02+0000\n" "Language-Team: Icelandic\n" "Language: is\n" "MIME-Version: 1.0\n" @@ -284,6 +284,7 @@ msgid "Deprecated" msgstr "Úrelt" #: issue/views.py:79 +#: wasa2il/templates/termsandconditions/tc_accept_terms.html:20 msgid "version" msgstr "útgáfa" @@ -585,7 +586,9 @@ msgid "My settings" msgstr "Stillingar" #: wasa2il/templates/base.html:83 +#: wasa2il/templates/registration/verification_invalid_entity.html:17 #: wasa2il/templates/registration/verification_needed.html:12 +#: wasa2il/templates/termsandconditions/tc_accept_terms.html:49 msgid "Logout" msgstr "Útskrá" @@ -626,7 +629,8 @@ msgstr "Við notum vafrakökur (e. cookie) til auðkenningar." #: wasa2il/templates/cookie_dialog.html:33 msgid "We do not use them to track you." -msgstr "Við notum þær ekki til þess að fylgjast með vefnotkun þinni." +msgstr "" +"Við notum þær ekki til þess að fylgjast með vefnotkun þinni." #: wasa2il/templates/cookie_dialog.html:38 msgid "I accept cookies being used for authentication purposes." @@ -2023,6 +2027,7 @@ msgstr "Þú getur núna reynt að innskrá þig!" #: wasa2il/templates/registration/activation_complete.html:11 #: wasa2il/templates/registration/login.html:28 +#: wasa2il/templates/registration/verification_duplicate.html:14 msgid "Login" msgstr "Innskrá" @@ -2284,6 +2289,32 @@ msgstr "" "Vinsamlegast reynið aftur og ef villan kemur aftur, hafið samband við " "umsjónarmann." +#: wasa2il/templates/registration/verification_age_limit.html:10 +msgid "Age below age limit" +msgstr "Lágmarksaldri ekki náð" + +#: wasa2il/templates/registration/verification_age_limit.html:12 +#, python-format +msgid "" +"We are sorry to inform you that you have not yet reached the minimum " +"required age of %(age_limit)s years. According to our information, you are " +"%(age)s years old." +msgstr "" +"Okkur þykir leitt að tilkynna þér að þú hefur ekki enn náð lágmarksaldri " +"fyrir aðgang, sem er %(age_limit)s ár. Samkvæmt okkar gögnum ertu %(age)s " +"ára." + +#: wasa2il/templates/registration/verification_age_limit.html:14 +msgid "" +"We will be very happy to provide you with access once you reach the minimum " +"required age." +msgstr "" +"Við veitum þér aðgang með mikilli gleði þegar þú hefur náð tilsettum aldri." + +#: wasa2il/templates/registration/verification_age_limit.html:16 +msgid "Front page" +msgstr "Forsíða" + #: wasa2il/templates/registration/verification_duplicate.html:6 msgid "Multiple accounts detected" msgstr "Fleiri en einn reikningur til" @@ -2312,6 +2343,24 @@ msgstr "Notandanafn" msgid "Email" msgstr "Netfang" +#: wasa2il/templates/registration/verification_invalid_entity.html:10 +msgid "Invalid entity" +msgstr "Ógildur aðili" + +#: wasa2il/templates/registration/verification_invalid_entity.html:12 +#, python-format +msgid "The entity %(name)s with the SSN %(ssn)s is an invalid entity." +msgstr "Aðilinn %(name)s með kennitöluna %(ssn)s er ógildur aðili." + +#: wasa2il/templates/registration/verification_invalid_entity.html:14 +msgid "Please try using a valid entity for verifying your account." +msgstr "" +"Vinsamlegast reyndu aftur með gildum aðila til að staðfesta aðganginn þinn." + +#: wasa2il/templates/registration/verification_invalid_entity.html:16 +msgid "Try again" +msgstr "Reyna aftur" + #: wasa2il/templates/registration/verification_needed.html:6 msgid "Verification needed" msgstr "Staðfestingar er þörf" @@ -2503,6 +2552,29 @@ msgstr "" msgid "User currently isn't volunteering for any tasks." msgstr "" +#: wasa2il/templates/termsandconditions/tc_accept_terms.html:19 +msgid "Terms and Conditions" +msgstr "Notkunarskilmálar" + +#: wasa2il/templates/termsandconditions/tc_accept_terms.html:23 +msgid "" +"In order to use this software, you must accept the following terms and " +"conditions." +msgstr "" +"Til að nota þennan hugbúnað verður þú að samþykkja eftirfarandi skilmála." + +#: wasa2il/templates/termsandconditions/tc_accept_terms.html:26 +msgid "Information" +msgstr "Upplýsingar" + +#: wasa2il/templates/termsandconditions/tc_accept_terms.html:44 +msgid "Accept all" +msgstr "Samþykkja alla" + +#: wasa2il/templates/termsandconditions/tc_accept_terms.html:46 +msgid "Accept" +msgstr "Samþykkja" + #: wasa2il/templates/topic/_topic_list_table.html:19 msgid "There are no topics in this polity!" msgstr "Það eru engir málaflokkar á þessu þingi!" @@ -2619,9 +2691,6 @@ msgstr "" #~ msgid "Issues in polity" #~ msgstr "Mál í þingi" -#~ msgid "Toggle information" -#~ msgstr "Sýna/Fela upplýsingar" - #~ msgid "Show all agreements" #~ msgstr "Sýna allar samþykktir" From e5c88960b34dbe1ae94ee8ff1b7c3c6a704f5d90 Mon Sep 17 00:00:00 2001 From: Helgi Hrafn Gunnarsson Date: Sun, 20 May 2018 21:07:28 +0000 Subject: [PATCH 7/7] Version: 0.10.1 -> 0.10.2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 57121573..5eef0f10 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.10.1 +0.10.2