Add base auth & a17t support

This commit is contained in:
R. Miles McCain 2020-04-10 15:20:14 -04:00
parent 8cb867f11b
commit 6c3064f3ea
No known key found for this signature in database
GPG Key ID: 91CB47BDDF2671A5
44 changed files with 572 additions and 4 deletions

0
shynet/a17t/__init__.py Normal file
View File

5
shynet/a17t/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class A17TConfig(AppConfig):
name = 'a17t'

View File

View File

@ -0,0 +1,2 @@
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/a17t@latest/dist/a17t.css">

View File

@ -0,0 +1,43 @@
{% load a17t_tags %}
<field role="field" class="block mb-2">
{% if field|is_checkbox %}
<label class="switch {{ classes.label }}">
{{ field }}
<span>{{ field.label }}</span>
</label>
{% elif field|is_radio %}
{% if field.auto_id %}
<label class="label my-1" for="{{field.auto_id}}">{{ field.label }}</label>
{% endif %}
{% for choice in field %}
<label class="switch">
{{ choice.tag }}
<span>{{ choice.choice_label }}</span>
</label>
{% endfor %}
{% elif field|is_input %}
<label class="label" for="{{field.auto_id}}">{{ field.label }}</label>
{{field|add_class:"input"}}
{% elif field|is_textarea %}
<label class="label" for="{{field.auto_id}}">{{ field.label }}</label>
{{ field|add_class:'textarea' }}
{% elif field|is_select %}
<label class="label" for="{{field.auto_id}}">{{ field.label }}</label>
<div class="select {% if field.errors|length > 0 %}~critical{% endif %}">
{{field}}
</div>
{% else %}
<label class="label" for="{{field.auto_id}}">{{ field.label }}</label>
{{field|add_class:"field"}}
{% endif %}
{% for error in field.errors %}
<p class="support ~critical">{{ error }}</p>
{% endfor %}
{% if field.help_text %}
<p class="support">{{field.help_text|safe}}</p>
{% endif %}
</field>

View File

@ -0,0 +1,15 @@
{% if form.non_field_errors %}
<aside class="aside ~critical content">
{% for non_field_error in form.non_field_errors %}
<p>{{ non_field_error }}</p>
{% endfor %}
</aside>
{% endif %}
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
{% for field in form.visible_fields %}
{% include 'a17t/includes/field.html' %}
{% endfor %}

View File

@ -0,0 +1,4 @@
{{ formset.management_form }}
{% for form in formset %}
{% include "a17t/includes/form.html" with form=form %}
{% endfor %}

View File

View File

@ -0,0 +1,96 @@
from django import forms
from django import template
from django.forms import BoundField
from django.template.loader import get_template
from django.utils.safestring import mark_safe
register = template.Library()
@register.filter
def a17t(element):
markup_classes = {"label": "", "value": "", "single_value": ""}
return render(element, markup_classes)
@register.filter
def a17t_inline(element):
markup_classes = {"label": "", "value": "", "single_value": ""}
return render(element, markup_classes)
def render(element, markup_classes):
if isinstance(element, BoundField):
template = get_template("a17t/includes/field.html")
context = {"field": element, "classes": markup_classes, "form": element.form}
else:
has_management = getattr(element, "management_form", None)
if has_management:
template = get_template("a17t/includes/formset.html")
context = {"formset": element, "classes": markup_classes}
else:
template = get_template("a17t/includes/form.html")
context = {"form": element, "classes": markup_classes}
return template.render(context)
@register.filter
def widget_type(field):
return field.field.widget
@register.filter
def is_select(field):
return isinstance(field.field.widget, forms.Select)
@register.filter
def is_multiple_select(field):
return isinstance(field.field.widget, forms.SelectMultiple)
@register.filter
def is_textarea(field):
return isinstance(field.field.widget, forms.Textarea)
@register.filter
def is_input(field):
return isinstance(
field.field.widget,
(
forms.TextInput,
forms.NumberInput,
forms.EmailInput,
forms.PasswordInput,
forms.URLInput,
),
)
@register.filter
def is_checkbox(field):
return isinstance(field.field.widget, forms.CheckboxInput)
@register.filter
def is_multiple_checkbox(field):
return isinstance(field.field.widget, forms.CheckboxSelectMultiple)
@register.filter
def is_radio(field):
return isinstance(field.field.widget, forms.RadioSelect)
@register.filter
def is_file(field):
return isinstance(field.field.widget, forms.FileInput)
@register.filter
def add_class(field, css_class):
if len(field.errors) > 0:
css_class += " ~critical"
return field.as_widget(attrs={"class": css_class})

View File

@ -0,0 +1,11 @@
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Account Inactive" %}{% endblock %}
{% block content %}
<h1>{% trans "Account Inactive" %}</h1>
<p>{% trans "This account is inactive." %}</p>
{% endblock %}

View File

@ -0,0 +1 @@
{% extends "base.html" %}

View File

@ -0,0 +1,80 @@
{% extends "account/base.html" %}
{% load i18n a17t_tags %}
{% block head_title %}{% trans "Email Addresses" %}{% endblock %}
{% block content %}
<h1 class="heading">{% trans "Email Addresses" %}</h1>
<hr class="sep">
<div class="card ~neutral !low">
{% if user.emailaddress_set.all %}
<p>{% trans 'The following email addresses are associated with your account:' %}</p>
<form action="{% url 'account_email' %}" class="email_list" method="post">
{% csrf_token %}
<fieldset class="blockLabels">
{% for emailaddress in user.emailaddress_set.all %}
<div class="ctrlHolder my-2">
<label for="email_radio_{{forloop.counter}}" class="switch">
<input id="email_radio_{{forloop.counter}}" type="radio" name="email"
{% if emailaddress.primary or user.emailaddress_set.count == 1 %}checked="checked" {%endif %}
value="{{emailaddress.email}}" />
{{ emailaddress.email }}
{% if emailaddress.verified %}
<span class="support ~positive">({% trans "Verified" %})</span>
{% else %}
<span class="support ~warning">({% trans "Unverified" %})</span>
{% endif %}
{% if emailaddress.primary %}<span class="support ~urge">({% trans "Primary" %})</span>{% endif %}
</label>
</div>
{% endfor %}
<div class="block">
<button class="button ~urge mb-1" type="submit" name="action_primary">{% trans 'Make Primary' %}</button>
<button class="button ~info mb-1" type="submit" name="action_send">{% trans 'Re-send Verification' %}</button>
<button class="button ~critical mb-1" type="submit" name="action_remove">{% trans 'Remove' %}</button>
</div>
</fieldset>
</form>
{% else %}
<aside class="aside ~warning">
<p>
{% trans "You currently do not have an email address associated with your account. Without one, you won't be able to reset your password, receive notifications, etc." %}
</p>
</aside>
{% endif %}
</div>
<hr class="sep">
<form method="post" action="{% url 'account_email' %}" class="add_email max-w-lg">
{% csrf_token %}
{{ form|a17t }}
<button name="button ~neutral !high" type="submit">{% trans "Add Email" %}</button>
</form>
{% endblock %}
{% block extra_body %}
<script type="text/javascript">
(function () {
var message = "{% trans 'Do you really want to remove the selected email address?' %}";
var actions = document.getElementsByName('action_remove');
if (actions.length) {
actions[0].addEventListener("click", function (e) {
if (!confirm(message)) {
e.preventDefault();
}
});
}
})();
</script>
{% endblock %}

View File

@ -0,0 +1,9 @@
{% load account %}{% user_display user as user_display %}{% load i18n %}{% autoescape off %}{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Hello from {{ site_name }}!
You're receiving this email because user {{ user_display }} has given yours as an email address to connect their account.
To confirm this is correct, go to {{ activate_url }}
{% endblocktrans %}
{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Thank you from {{ site_name }}!
{{ site_domain }}{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1 @@
{% include "account/email/email_confirmation_message.txt" %}

View File

@ -0,0 +1 @@
{% include "account/email/email_confirmation_subject.txt" %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Please Confirm Your Email Address{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,12 @@
{% load i18n %}{% autoescape off %}{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Hello from {{ site_name }}!
You're receiving this email because you or someone else has requested a password for your user account.
It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktrans %}
{{ password_reset_url }}
{% if username %}{% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %}
{% endif %}{% blocktrans with site_name=current_site.name site_domain=current_site.domain %}Thank you for using {{ site_name }}!
{{ site_domain }}{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,4 @@
{% load i18n %}
{% autoescape off %}
{% blocktrans %}Password Reset Email{% endblocktrans %}
{% endautoescape %}

View File

@ -0,0 +1,34 @@
{% extends "account/base.html" %}
{% load i18n %}
{% load account %}
{% block head_title %}{% trans "Confirm Email Address" %}{% endblock %}
{% block content %}
<h1>{% trans "Confirm Email Address" %}</h1>
{% if confirmation %}
{% user_display confirmation.email_address.user as user_display %}
<p>{% blocktrans with confirmation.email_address.email as email %}Please confirm that <a
href="mailto:{{ email }}">{{ email }}</a> is a valid email where we can reach you.{% endblocktrans %}
</p>
<form method="post" action="{% url 'account_confirm_email' confirmation.key %}">
{% csrf_token %}
<button type="submit" class="button ~positive !high">{% trans 'Confirm' %}</button>
</form>
{% else %}
{% url 'account_email' as email_url %}
<p>{% blocktrans %}This email confirmation link expired or is invalid. Please <a href="{{ email_url }}">issue a new
email confirmation request</a>.{% endblocktrans %}</p>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "account/base.html" %}
{% load i18n a17t_tags %}
{% load account socialaccount %}
{% block head_title %}{% trans "Sign In" %}{% endblock %}
{% block content %}
<div class="card ~neutral !low max-w-lg">
<h1 class="heading mb-3">{% trans "Sign In" %}</h1>
<form class="login" method="POST" action="{% url 'account_login' %} max-w-lg">
{% csrf_token %}
{{ form|a17t }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<br>
<button class="button ~urge !high mr-2" type="submit">{% trans "Sign In" %}</button>
<a href="{% url 'account_reset_password' %}" class="button ~neutral mr-2">{% trans "Reset Password" %}</a>
<a href="{{ signup_url }}" class="button ~neutral">Sign Up</a>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "account/base.html" %}
{% load i18n a17t_tags %}
{% block head_title %}{% trans "Sign Out" %}{% endblock %}
{% block content %}
<h1>{% trans "Sign Out" %}</h1>
<p>{% trans 'Are you sure you want to sign out?' %}</p>
<form method="post" action="{% url 'account_logout' %}" class="max-w-lg">
{% csrf_token %}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
{% endif %}
<button type="submit" class="button ~urge !high">{% trans 'Sign Out' %}</button>
</form>
{% endblock %}

View File

@ -0,0 +1,2 @@
{% load i18n %}
{% blocktrans %}You cannot remove your primary email address ({{email}}).{% endblocktrans %}

View File

@ -0,0 +1,2 @@
{% load i18n %}
{% blocktrans %}Confirmation email sent to {{email}}.{% endblocktrans %}

View File

@ -0,0 +1,2 @@
{% load i18n %}
{% blocktrans %}You have confirmed {{email}}.{% endblocktrans %}

View File

@ -0,0 +1,2 @@
{% load i18n %}
{% blocktrans %}Removed email address {{email}}.{% endblocktrans %}

View File

@ -0,0 +1,4 @@
{% load account %}
{% load i18n %}
{% user_display user as name %}
{% blocktrans %}Successfully signed in as {{name}}.{% endblocktrans %}

View File

@ -0,0 +1,2 @@
{% load i18n %}
{% blocktrans %}You have signed out.{% endblocktrans %}

View File

@ -0,0 +1,2 @@
{% load i18n %}
{% blocktrans %}Password successfully changed.{% endblocktrans %}

View File

@ -0,0 +1,2 @@
{% load i18n %}
{% blocktrans %}Password successfully set.{% endblocktrans %}

View File

@ -0,0 +1,2 @@
{% load i18n %}
{% blocktrans %}Primary email address set.{% endblocktrans %}

View File

@ -0,0 +1,2 @@
{% load i18n %}
{% blocktrans %}Your primary email address must be verified.{% endblocktrans %}

View File

@ -0,0 +1,15 @@
{% extends "account/base.html" %}
{% load i18n a17t_tags %}
{% block head_title %}{% trans "Change Password" %}{% endblock %}
{% block content %}
<h1>{% trans "Change Password" %}</h1>
<form method="POST" action="{% url 'account_change_password' %}" class="password_change max-w-lg">
{% csrf_token %}
{{ form|a17t }}
<button type="submit" name="action">{% trans "Change Password" %}</button>
</form>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "account/base.html" %}
{% load i18n a17t_tags %}
{% load account %}
{% block head_title %}{% trans "Password Reset" %}{% endblock %}
{% block content %}
<h1>{% trans "Password Reset" %}</h1>
{% if user.is_authenticated %}
{% include "account/snippets/already_logged_in.html" %}
{% endif %}
<p>
{% trans "Forgotten your password? Enter your email address below, and we'll send you an email to reset it." %}
</p>
<form method="POST" action="{% url 'account_reset_password' %}" class="password_reset max-w-lg">
{% csrf_token %}
{{ form|a17t }}
<button type="submit" class="button ~urge !high">{% trans 'Reset Password' %}</button>
</form>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends "account/base.html" %}
{% load i18n %}
{% load account %}
{% block head_title %}{% trans "Password Reset" %}{% endblock %}
{% block content %}
<h1>{% trans "Password Reset" %}</h1>
{% if user.is_authenticated %}
{% include "account/snippets/already_logged_in.html" %}
{% endif %}
<p>{% blocktrans %}We have sent you an email. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}</p>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "account/base.html" %}
{% load i18n a17t_tags %}
{% block head_title %}{% trans "Change Password" %}{% endblock %}
{% block content %}
<h1>{% if token_fail %}{% trans "Bad Token" %}{% else %}{% trans "Change Password" %}{% endif %}</h1>
{% if token_fail %}
{% url 'account_reset_password' as passwd_reset_url %}
<p>{% blocktrans %}The password reset link was invalid, possibly because it has already been used. Please request a <a href="{{ passwd_reset_url }}">new password reset</a>.{% endblocktrans %}</p>
{% else %}
{% if form %}
<form method="POST" action="{{ action_url }}" class="max-w-lg">
{% csrf_token %}
{{ form|a17t }}
<button type="submit" name="action" class="button ~urge !high">{% trans 'Change Password'}</button>
</form>
{% else %}
<p>{% trans 'Your password is now changed.' %}</p>
{% endif %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,9 @@
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Change Password" %}{% endblock %}
{% block content %}
<h1>{% trans "Change Password" %}</h1>
<p>{% trans 'Your password is now changed.' %}</p>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends "account/base.html" %}
{% load i18n a17t_tags %}
{% block head_title %}{% trans "Set Password" %}{% endblock %}
{% block content %}
<h1>{% trans "Set Password" %}</h1>
<form method="POST" action="{% url 'account_set_password' %}" class="password_set max-w-lg">
{% csrf_token %}
{{ form|a17t }}
<button type="submit" name="action" class="button ~urge !high">{% trans 'Set Password' %}</button>
</form>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "account/base.html" %}
{% load i18n a17t_tags %}
{% block head_title %}{% trans "Signup" %}{% endblock %}
{% block content %}
<h1>{% trans "Sign Up" %}</h1>
<p>{% blocktrans %}Already have an account? Then please <a href="{{ login_url }}">sign in</a>.{% endblocktrans %}</p>
<form class="signup max-w-lg" id="signup_form" method="post" action="{% url 'account_signup' %}">
{% csrf_token %}
{{ form|a17t }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button type="submit" class="button ~urge !high">{% trans "Sign Up" %} &raquo;</button>
</form>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Sign Up Closed" %}{% endblock %}
{% block content %}
<h1>{% trans "Sign Up Closed" %}</h1>
<p>{% trans "Public sign-ups are not allowed at this time." %}</p>
{% endblock %}

View File

@ -0,0 +1,5 @@
{% load i18n %}
{% load account %}
{% user_display user as user_display %}
<p><strong>{% trans "Note" %}:</strong> {% blocktrans %}you are already logged in as {{ user_display }}.{% endblocktrans %}</p>

View File

@ -0,0 +1,12 @@
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Verify Your Email Address" %}{% endblock %}
{% block content %}
<h1>{% trans "Verify Your Email Address" %}</h1>
<p>{% blocktrans %}We have sent an email to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}</p>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "account/base.html" %}
{% load i18n %}
{% block head_title %}{% trans "Verify Your Email Address" %}{% endblock %}
{% block content %}
<h1>{% trans "Verify Your Email Address" %}</h1>
{% url 'account_email' as email_url %}
<p>{% blocktrans %}This part of the site requires us to verify that
you are who you claim to be. For this purpose, we require that you
verify ownership of your email address. {% endblocktrans %}</p>
<p>{% blocktrans %}We have sent an email to you for
verification. Please click on the link inside this email. Please
contact us if you do not receive it within a few minutes.{% endblocktrans %}</p>
<p>{% blocktrans %}<strong>Note:</strong> you can still <a href="{{ email_url }}">change your email address</a>.{% endblocktrans %}</p>
{% endblock %}

View File

@ -6,8 +6,7 @@
<title>{% block head_title %}Privacy-oriented analytics{% endblock %} | Shynet</title> <title>{% block head_title %}Privacy-oriented analytics{% endblock %} | Shynet</title>
<meta name="robots" content="noindex"> <meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet"> {% include 'a17t/head.html' %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/a17t@latest/dist/a17t.css">
{% block extra_head %} {% block extra_head %}
{% endblock %} {% endblock %}
</head> </head>
@ -19,7 +18,7 @@
{% if messages %} {% if messages %}
<div> <div>
{% for message in messages %} {% for message in messages %}
<article class="card {{message.tags}} mb-2 w-full">{{message}}</article> <article class="card {{message.tags}} !high mb-2 w-full">{{message}}</article>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
@ -29,7 +28,7 @@
<strong>Menu:</strong> <strong>Menu:</strong>
<ul> <ul>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li><a href="{% url 'account_email' %}">Change E-mail</a></li> <li><a href="{% url 'account_email' %}">Change Email</a></li>
<li><a href="{% url 'account_logout' %}">Sign Out</a></li> <li><a href="{% url 'account_logout' %}">Sign Out</a></li>
{% else %} {% else %}
<li><a href="{% url 'account_login' %}">Sign In</a></li> <li><a href="{% url 'account_login' %}">Sign In</a></li>

View File

@ -38,6 +38,7 @@ INSTALLED_APPS = [
"django.contrib.messages", "django.contrib.messages",
"django.contrib.staticfiles", "django.contrib.staticfiles",
"django.contrib.sites", "django.contrib.sites",
"a17t",
"core", "core",
"analytics", "analytics",
"allauth", "allauth",
@ -148,3 +149,12 @@ CELERY_REDIS_SOCKET_TIMEOUT = 15
MAXMIND_CITY_DB = os.getenv("MAXMIND_CITY_DB") MAXMIND_CITY_DB = os.getenv("MAXMIND_CITY_DB")
MAXMIND_ASN_DB = os.getenv("MAXMIND_ASN_DB") MAXMIND_ASN_DB = os.getenv("MAXMIND_ASN_DB")
# Messages
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
messages.INFO: '~info',
messages.WARNING: "~warning",
messages.ERROR: "~critical",
messages.SUCCESS: "~positive",
}