Merge branch 'master' into production

This commit is contained in:
CDaut 2022-06-27 23:39:22 +02:00 committed by CDaut
commit 8a63661800
13 changed files with 251 additions and 6 deletions

View file

@ -10,6 +10,7 @@ from django.shortcuts import render, redirect
from blog.models import Topic, Tag, Blogpost
from django.template import Template, Context
from django.views.decorators.csrf import csrf_exempt
from django_2fa.decorators import mfa_login_required
def render_md_file(path) -> Template:
@ -53,6 +54,7 @@ def index(request) -> HttpResponse:
@login_required
@mfa_login_required
def edit(request, id) -> HttpResponse:
blogpost = Blogpost.objects.get(pk=id)
mdfile_content = open(blogpost.mdfile, "r").read()
@ -94,6 +96,7 @@ def edit(request, id) -> HttpResponse:
@login_required
@csrf_exempt
@mfa_login_required
def order(request) -> HttpResponse:
if request.method == "POST":
root_id = int(request.POST['rootID']) if request.POST['rootID'] != 'root_list' else None
@ -114,6 +117,7 @@ def order(request) -> HttpResponse:
@login_required
@mfa_login_required
def addpost(request) -> HttpResponse:
context = {'alltopics': Topic.objects.all().order_by('name').values(), 'markdown': '',
'roottopics': Topic.objects.all().filter(rootTopic=None),
@ -154,6 +158,7 @@ def addpost(request) -> HttpResponse:
# @login_required
# @mfa_login_required
# def createmocks(request, objtype, n) -> HttpResponse:
# topics = TopicFactory.create_batch(n)
#
@ -171,6 +176,7 @@ def addpost(request) -> HttpResponse:
@login_required
@mfa_login_required
def addtopic(request):
context = {'roottopics': Topic.objects.all().filter(rootTopic=None), 'allposts': Blogpost.objects.all()}

View file

@ -32,6 +32,8 @@ INSTALLED_APPS = [
'blog',
'markdownblog',
'fontawesomefree',
'django_2fa',
'django_extensions',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
@ -48,6 +50,7 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django_2fa.middleware.MFAProctectMiddleware',
]
ROOT_URLCONF = 'markdownblog.urls'
@ -129,9 +132,21 @@ STATIC_ROOT = '/markdownblog/static'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_URL = '/accounts/login'
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/"
CSRF_HEADER_NAME = "X-CSRFToken"
CSRF_TRUSTED_ORIGINS = ["https://cdaut.de", "http://cdaut.de"]
# multifactor auth
MFA_URL = '/accounts/2fa/login'
SALT_KEY = os.environ['SALT_KEY']
MFA_ISSUER_NAME = 'MDBlog'
# Configure mail here
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

View file

@ -24,4 +24,8 @@ header, main, footer {
header, main, footer {
padding-left: 0;
}
}
.mt-3 {
margin-top: 3em;
}

View file

@ -0,0 +1,57 @@
{% extends 'base/base.html' %}
{% load i18n static %}
{% block title %}
Add new 2FA device
{% endblock %}
{% block includehere %}
{% endblock %}
{% block content %}
<div class="col s12">
<h3>{% translate 'Add new 2FA device' %}</h3>
<hr>
{% if form.errors and not form.non_field_errors %}
<p class="errornote">
{% if form.errors.items|length == 1 %}{% translate "Please correct the error below." %}{% else %}
{% translate "Please correct the errors below." %}{% endif %}
</p>
{% endif %}
<form action="{{ request.path }}" method="post" id="add-device-form" class="col s12">
{% csrf_token %}
<input type="hidden" name="{{ next_field }}" value="{{ next }}">
{% if form.non_field_errors %}
<div class="form-row">
{{ form.non_field_errors }}
</div>
{% endif %}
<div class="row">
<div class="input-field col s12 l6">
{{ form.name.errors }}
{{ form.name }}
{{ form.name.label_tag }}
</div>
</div>
<div class="row">
<div class="input-field col s12 l6">
{{ form.device_type.errors }}
{{ form.device_type }}
{{ form.device_type.label_tag }}
</div>
</div>
{% for hidden in form.hidden_fields %}{{ hidden }}{% endfor %}
<div class="row">
<div class="input-field col s12 l6">
<button class="btn waves-effect waves-light" type="submit">
{% translate 'Continue' %}
</button>
</div>
</div>
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
var elems = document.querySelectorAll('select');
var instances = M.FormSelect.init(elems);
});
</script>
{% endblock %}

View file

@ -0,0 +1,41 @@
{% extends "base/base.html" %}
{% load i18n static %}
{% block title %}{% translate "Complete setup for 2FA Device" %}{% endblock %}
{% block content %}
<div id="content-main" class="col s12">
<h3>{% translate 'Hardware Key Setup' %}</h3>
<hr>
<h4 class="tc">{% translate 'Insert your hardware key to complete setup for:' %} {{ device.name }}</h4>
</div>
<script
src="{% static '2fa/axios.min.js' %}"></script>
<script src="{% static '2fa/cbor.js' %}"></script>
<script>
function start_reg() {
axios.post("{% url 'django_2fa:fido-reg-begin' device.id %}", {}, {responseType: 'arraybuffer'})
.then((response) => {
return CBOR.decode(response.data);
})
.then((options) => {
return navigator.credentials.create(options);
})
.then((attestation) => {
console.log(attestation);
var data = CBOR.encode({
"attestationObject": new Uint8Array(attestation.response.attestationObject),
"clientDataJSON": new Uint8Array(attestation.response.clientDataJSON)
});
return axios.post("{% url 'django_2fa:fido-reg-complete' device.id %}", data, {});
})
.then((response) => {
location.href = "{{ next }}";
})
.catch((e) => {
console.error(e);
alert('{% translate "Error in hardware key registration" %}');
});
}
start_reg();
</script>
{% endblock %}

View file

@ -0,0 +1,39 @@
{% extends 'base/base.html' %}
{% load i18n static %}
{% block title %}
Manage 2FA devices
{% endblock %}
{% block includehere %}
{% endblock %}
{% block content %}
<div id="content-main" class="col s12">
<h3>{% translate 'Manage 2FA devices' %}</h3>
<hr>
<a href="{% url 'django_2fa:device-add' %}" class="waves-effect waves-light btn mt-3">
Add Device
</a>
<ul class="collection">
{% for d in devices %}
<li class="collection-item avatar">
<i class="material-icons circle">
{% if d.device_type == 'email' %}mail
{% elif d.device_type == 'app' %}phone_android
{% else %}vpn_key{% endif %}
</i>
<span class="title"><strong>{{ d.name }} - {{ d.get_device_type_display }}</strong></span>
<p>
{% if not d.setup_complete %}
<a href="{% url 'django_2fa:device-complete' d.id %}">Complete Setup</a><br>
{% endif %}
Added: {{ d.created|date:"D, dS M Y" }}
</p>
<a href="{% url 'django_2fa:device-remove' d.id %}" title="remove" class="secondary-content"
onclick="return confirm('Are sure you wish to remove this device?')">
<i class="material-icons">delete</i>
</a>
</li>
{% endfor %}
</ul>
</div>
{% endblock %}

View file

@ -0,0 +1,76 @@
{% extends "base/base.html" %}
{% load i18n static %}
{% block title %}{% translate "Verify authenticator app" %}{% endblock %}
{% block content %}
<div class="col s12">
{% if complete_setup %}
<h3>{% translate 'Verify your authorization device.' %}</h3>
{% else %}
<h3>{% translate 'Please authenticate with your 2FA token' %}</h3>
{% endif %}
<hr>
{% if form.errors and not form.non_field_errors %}
<p class="errornote">
{% if form.errors.items|length == 1 %}{% translate "Please correct the error below." %}{% else %}
{% translate "Please correct the errors below." %}{% endif %}
</p>
{% endif %}
<div class="row">
<form action="{{ request.path }}" method="post" id="mfa-form" class="col s12">
{% csrf_token %}
<input type="hidden" name="{{ next_field }}" value="{{ next }}">
{% if form.non_field_errors %}
<div class="form-row">
{{ form.non_field_errors }}
</div>
{% endif %}
<div class="row">
<b><p>{% translate '2FA Factor:' %} {{ device.name }}</p></b>
<p>
{% if device.device_type == 'email' %}
{% translate 'Check your e-mail for your authorization code.' %}
{% elif device.device_type == 'app' %}
{% if not complete_setup %}
{% translate 'Enter code from your authenicator application.' %}{% endif %}
{% else %}
{% translate 'Use your hardware token to complete sign in.' %}
{% endif %}
</p>
</div>
<div class="row">
{% if complete_setup and device.device_type == 'app' %}
<p>
{% translate 'Scan QR below in your authenicator app, then enter a code to verify your device.' %}
<br>
<canvas id="qr"></canvas>
<br><br><strong>{% translate 'Provision URL:' %}</strong><br>
<pre>{{ device.provision_url }}</pre>
</p>
{% endif %}
<div class="input-field col s12 l6">
{{ form.mfa_code.errors }}
{{ form.mfa_code }}
{{ form.mfa_code.label_tag }}
</div>
</div>
<div class="row">
<div class="input-field col s12 l6">
<button class="btn waves-effect waves-light" type="submit">{% translate 'Continue' %}</button>
</div>
</div>
</form>
</div>
</div>
{% if complete_setup and device.device_type == 'app' %}
<script src="{% static '2fa/qrious.min.js' %}"></script>
<script>
(function () {
var qr = new QRious({
element: document.getElementById('qr'),
value: '{{ device.provision_url }}',
size: 200
});
})();
</script>
{% endif %}
{% endblock %}

View file

@ -61,6 +61,8 @@
</li>
<li class="no-liststyle"><a class="waves-effect" href="{% url 'admin:index' %}"><i class="material-icons">
admin_panel_settings</i>Admin panel</a></li>
<li class="no-liststyle"><a class="waves-effect" href="{% url 'django_2fa:devices' %}"><i class="material-icons">
vpn_key</i>Manage 2FA</a></li>
<li class="no-liststyle"><a class="waves-effect" href="{% url 'logout' %}"><i
class="material-icons">logout</i>Logout</a></li>
{% if debug %}

View file

@ -6,6 +6,7 @@
{% block includehere %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
<link rel="stylesheet" href="{% static 'addpost.css' %}">
<link rel="stylesheet" href="{% static 'viewblog.css' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/highlight.js/latest/styles/github.min.css">
{% endblock %}
{% block content %}
@ -13,8 +14,7 @@
<form action="{% url 'addpost' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<input name="title" type="text" class="posttitle" id="input_title" value="Title">
<textarea name="markdown" id="mde">
</textarea>
<textarea name="markdown" id="mde"></textarea>
<div class="row">
<div class="col s6">
<label>

View file

@ -1,8 +1,11 @@
from django.contrib import admin
from django.urls import path, include
import django_2fa.urls
urlpatterns = [
path('', include('blog.urls')),
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls'))
path('accounts/', include('django.contrib.auth.urls')),
path('accounts/2fa/', include(django_2fa.urls)),
]

View file

@ -5,3 +5,5 @@ markdown2==2.4.3
fontawesomefree==6.1.1
pygments==2.12.0
gunicorn==20.1.0
django-2fa==0.9.0
django-extensions==3.1.5