mirror of
https://github.com/MOIS3Y/logs-collector.git
synced 2025-02-01 01:10:52 +01:00
Create: MVP 2fa account app
This commit is contained in:
parent
2e648ac4fe
commit
30b3efa5fc
0
logs_collector/account/__init__.py
Normal file
0
logs_collector/account/__init__.py
Normal file
3
logs_collector/account/admin.py
Normal file
3
logs_collector/account/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
6
logs_collector/account/apps.py
Normal file
6
logs_collector/account/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AccountConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'account'
|
0
logs_collector/account/migrations/__init__.py
Normal file
0
logs_collector/account/migrations/__init__.py
Normal file
3
logs_collector/account/models.py
Normal file
3
logs_collector/account/models.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
3
logs_collector/account/tests.py
Normal file
3
logs_collector/account/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
40
logs_collector/account/urls.py
Normal file
40
logs_collector/account/urls.py
Normal file
@ -0,0 +1,40 @@
|
||||
from django.conf import settings
|
||||
from django.urls import path
|
||||
from django.contrib.auth.views import LogoutView
|
||||
|
||||
from rest_framework_simplejwt.views import (
|
||||
TokenObtainPairView,
|
||||
TokenRefreshView,
|
||||
TokenVerifyView
|
||||
)
|
||||
|
||||
|
||||
app_name = 'account'
|
||||
|
||||
urlpatterns = [
|
||||
# WEB LOGOUT:
|
||||
path(
|
||||
'accounts/logout/',
|
||||
LogoutView.as_view(next_page=settings.LOGOUT_REDIRECT_URL),
|
||||
name='logout'
|
||||
)
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
# JWT AUTH:
|
||||
path(
|
||||
'api/v1/auth/token/',
|
||||
TokenObtainPairView.as_view(),
|
||||
name='token_obtain_pair'
|
||||
),
|
||||
path(
|
||||
'api/v1/auth/token/refresh/',
|
||||
TokenRefreshView.as_view(),
|
||||
name='token_refresh'
|
||||
),
|
||||
path(
|
||||
'api/v1/auth/token/verify/',
|
||||
TokenVerifyView.as_view(),
|
||||
name='token_verify'
|
||||
),
|
||||
]
|
46
logs_collector/account/utils.py
Normal file
46
logs_collector/account/utils.py
Normal file
@ -0,0 +1,46 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import resolve_url
|
||||
from django.urls import reverse
|
||||
from django.utils.http import url_has_allowed_host_and_scheme # renamed Dj^3.*
|
||||
from two_factor.admin import AdminSiteOTPRequired, AdminSiteOTPRequiredMixin
|
||||
|
||||
|
||||
# https://stackoverflow.com/questions/48600737/django-two-factor-auth-cant-access-admin-site
|
||||
class AdminSiteOTPRequiredMixinRedirectSetup(AdminSiteOTPRequired):
|
||||
"""
|
||||
Fixes the current implementation of django-two-factor-auth = 1.15.3
|
||||
when admin page is patched for 2fa
|
||||
(circular redirect - super user created with manage.py
|
||||
and cannot log in because he does not have a device configured).
|
||||
The class redirects to the setup page.
|
||||
After that, you can log in as usual.
|
||||
"""
|
||||
def login(self, request, extra_context=None):
|
||||
redirect_to = request.POST.get(
|
||||
REDIRECT_FIELD_NAME, request.GET.get(REDIRECT_FIELD_NAME)
|
||||
)
|
||||
# For users not yet verified the AdminSiteOTPRequired.has_permission
|
||||
# will fail. So use the standard admin has_permission check:
|
||||
# (is_active and is_staff) and then check for verification.
|
||||
# Go to index if they pass, otherwise make them setup OTP device.
|
||||
if request.method == "GET" and super(
|
||||
AdminSiteOTPRequiredMixin, self
|
||||
).has_permission(request):
|
||||
# Already logged-in and verified by OTP
|
||||
if request.user.is_verified():
|
||||
# User has permission
|
||||
index_path = reverse("admin:index", current_app=self.name)
|
||||
else:
|
||||
# User has permission but no OTP set:
|
||||
index_path = reverse("two_factor:setup", current_app=self.name)
|
||||
return HttpResponseRedirect(index_path)
|
||||
|
||||
if not redirect_to or not url_has_allowed_host_and_scheme(
|
||||
url=redirect_to, allowed_hosts=[request.get_host()]
|
||||
):
|
||||
redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)
|
||||
|
||||
return redirect_to_login(redirect_to)
|
0
logs_collector/account/views.py
Normal file
0
logs_collector/account/views.py
Normal file
@ -2,39 +2,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-bs-theme="auto">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="{% static 'collector/css/bootstrap.min.css' %}"
|
||||
rel="stylesheet"
|
||||
>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="{% static 'collector/img/apple-touch-icon.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="{% static 'collector/img/favicon-32x32.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="{% static 'collector/img/favicon-16x16.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="manifest"
|
||||
href="{% static 'collector/img/site.webmanifest' %}"
|
||||
>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css"
|
||||
>
|
||||
{% block title %}{% endblock title %}
|
||||
{% include 'collector/includes/metalinks.html' %}
|
||||
<title>{% block title %}{% endblock title %}</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
|
@ -0,0 +1,33 @@
|
||||
{% load static %}
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="{% static 'collector/css/bootstrap.min.css' %}"
|
||||
rel="stylesheet"
|
||||
>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="{% static 'collector/img/apple-touch-icon.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="{% static 'collector/img/favicon-32x32.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="{% static 'collector/img/favicon-16x16.png' %}"
|
||||
>
|
||||
<link
|
||||
rel="manifest"
|
||||
href="{% static 'collector/img/site.webmanifest' %}"
|
||||
>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css"
|
||||
>
|
@ -1,20 +1,5 @@
|
||||
{% load collector_extras %}
|
||||
{% get_platforms as platforms %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
|
||||
<symbol id="check2" viewBox="0 0 16 16">
|
||||
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
|
||||
</symbol>
|
||||
<symbol id="circle-half" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/>
|
||||
</symbol>
|
||||
<symbol id="moon-stars-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
|
||||
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"/>
|
||||
</symbol>
|
||||
<symbol id="sun-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
|
||||
</symbol>
|
||||
</svg>
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<div class="container">
|
||||
<a
|
||||
@ -114,7 +99,13 @@
|
||||
</li>
|
||||
<li><button class="dropdown-item" type="button"><i class="bi bi-gear"></i> Settings</button></li>
|
||||
<li><hr class="dropdown-divider" /></li>
|
||||
<li><button class="dropdown-item" type="button"><i class="bi bi-door-closed"></i> Logout</button></li>
|
||||
<li>
|
||||
<a
|
||||
href="{% url 'account:logout' %}"
|
||||
class="dropdown-item"
|
||||
type="button"><i class="bi bi-door-closed"></i> Logout
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
@ -123,7 +114,7 @@
|
||||
<div class="btn-group">
|
||||
<a
|
||||
type="button"
|
||||
href="{% url 'admin:index' %}"
|
||||
href="{% url 'two_factor:login' %}"
|
||||
class="btn btn-outline-secondary"
|
||||
>
|
||||
<i class="bi bi-box-arrow-in-right"></i></i> Login
|
||||
@ -139,42 +130,7 @@
|
||||
<!-- Theme switcher-->
|
||||
<li class="nav-item dropdown">
|
||||
<div class="dropdown bd-mode-toggle">
|
||||
<button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
|
||||
id="bd-theme"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-label="Toggle theme (auto)">
|
||||
<svg class="bi my-1 theme-icon-active" width="1em" height="1em"><use href="#circle-half"></use></svg>
|
||||
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
|
||||
<li>
|
||||
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light" aria-pressed="false">
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#sun-fill"></use></svg>
|
||||
Light
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false">
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#moon-stars-fill"></use></svg>
|
||||
Dark
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center active"
|
||||
data-bs-theme-value="auto" aria-pressed="true"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#circle-half"></use></svg>
|
||||
Auto
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
{% include 'collector/includes/theme_swither.html' %}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -0,0 +1,91 @@
|
||||
<!--Theme switcher icons-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
|
||||
<symbol id="check2" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id="circle-half" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
|
||||
</symbol>
|
||||
<symbol id="moon-stars-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"
|
||||
/>
|
||||
<path
|
||||
d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id="sun-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"
|
||||
/>
|
||||
</symbol>
|
||||
</svg>
|
||||
<!--Theme switcher buttons-->
|
||||
<button
|
||||
class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
|
||||
id="bd-theme"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-label="Toggle theme (auto)"
|
||||
>
|
||||
<svg class="bi my-1 theme-icon-active" width="1em" height="1em">
|
||||
<use href="#circle-half"></use>
|
||||
</svg>
|
||||
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu dropdown-menu-end shadow"
|
||||
aria-labelledby="bd-theme-text"
|
||||
>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="light"
|
||||
aria-pressed="false"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em">
|
||||
<use href="#sun-fill"></use>
|
||||
</svg>
|
||||
Light
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||
<use href="#check2"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="dark"
|
||||
aria-pressed="false"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em">
|
||||
<use href="#moon-stars-fill"></use>
|
||||
</svg>
|
||||
Dark
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||
<use href="#check2"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center active"
|
||||
data-bs-theme-value="auto"
|
||||
aria-pressed="true"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em">
|
||||
<use href="#circle-half"></use>
|
||||
</svg>
|
||||
Auto
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||
<use href="#check2"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
After Width: | Height: | Size: 3.9 KiB |
@ -1,7 +1,7 @@
|
||||
{% extends 'collector/base.html' %}
|
||||
{% load static %}
|
||||
{% load collector_extras %}
|
||||
{% block title %}<title>{{ title }}</title>{% endblock title %}
|
||||
{% block title %} {{ title }} {% endblock title %}
|
||||
{% block main %}
|
||||
<div class="container mt-3">
|
||||
<div class="row">
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% extends 'collector/base.html' %}
|
||||
{% load static %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% block title %}<title>{{ title }}</title>{% endblock title %}
|
||||
{% block title %} {{ title }} {% endblock title %}
|
||||
{% block main %}
|
||||
<div class="container mt-3">
|
||||
<div class="card">
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% extends 'collector/base.html' %}
|
||||
{% load static %}
|
||||
{% load collector_extras %}
|
||||
{% block title %}<title>{{ title }}</title>{% endblock title %}
|
||||
{% block title %} {{ title }} {% endblock title %}
|
||||
{% block main %}
|
||||
<div class="container mt-3">
|
||||
{% csrf_token %}
|
||||
|
@ -15,6 +15,8 @@ from rest_framework import filters
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
|
||||
from two_factor.views import OTPRequiredMixin
|
||||
|
||||
from .models import Archive, Ticket, Platform
|
||||
from .forms import TicketForm
|
||||
from .filters import ArchiveFilter, TicketFilter
|
||||
@ -29,7 +31,11 @@ from .serializers import (
|
||||
)
|
||||
|
||||
|
||||
class ArchiveHandlerView(LoginRequiredMixin, SingleObjectMixin, generic.View):
|
||||
class ArchiveHandlerView(
|
||||
OTPRequiredMixin,
|
||||
LoginRequiredMixin,
|
||||
SingleObjectMixin,
|
||||
generic.View):
|
||||
model = Archive
|
||||
slug_field = 'file'
|
||||
slug_url_kwarg = 'path'
|
||||
@ -67,7 +73,7 @@ class UpdateTicket(LoginRequiredMixin, PageTitleViewMixin, generic.UpdateView):
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ListAllTickets(PageTitleViewMixin, generic.ListView):
|
||||
class ListAllTickets(LoginRequiredMixin, PageTitleViewMixin, generic.ListView):
|
||||
model = Ticket
|
||||
template_name = 'collector/tickets.html'
|
||||
context_object_name = 'tickets'
|
||||
|
@ -36,12 +36,18 @@ INSTALLED_APPS = [
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'collector.apps.CollectorConfig', # main app
|
||||
'account.apps.AccountConfig', # account app
|
||||
'rest_framework',
|
||||
'rest_framework_simplejwt',
|
||||
'django_filters',
|
||||
'drf_spectacular',
|
||||
"crispy_forms",
|
||||
"crispy_bootstrap5",
|
||||
'django_otp',
|
||||
'django_otp.plugins.otp_static',
|
||||
'django_otp.plugins.otp_totp',
|
||||
'two_factor.plugins.phonenumber', # <- if you want phone number capability
|
||||
'two_factor',
|
||||
'django_cleanup.apps.CleanupConfig', # required bottom
|
||||
]
|
||||
|
||||
@ -51,6 +57,7 @@ MIDDLEWARE = [
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django_otp.middleware.OTPMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
@ -60,7 +67,7 @@ ROOT_URLCONF = 'logs_collector.urls'
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
@ -216,3 +223,7 @@ SIMPLE_JWT = {
|
||||
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer", # noqa:E501
|
||||
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer", # noqa:E501
|
||||
}
|
||||
|
||||
LOGIN_URL = 'two_factor:login'
|
||||
LOGIN_REDIRECT_URL = 'collector:index'
|
||||
LOGOUT_REDIRECT_URL = 'two_factor:login'
|
||||
|
@ -1,60 +1,28 @@
|
||||
"""
|
||||
URL configuration for logs_collector project.
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/4.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
|
||||
from rest_framework_simplejwt.views import (
|
||||
TokenObtainPairView,
|
||||
TokenRefreshView,
|
||||
TokenVerifyView
|
||||
)
|
||||
|
||||
from drf_spectacular.views import (
|
||||
SpectacularAPIView,
|
||||
SpectacularRedocView,
|
||||
SpectacularSwaggerView
|
||||
)
|
||||
|
||||
from two_factor.urls import urlpatterns as tf_urls
|
||||
|
||||
from logs_collector import settings
|
||||
from account.utils import AdminSiteOTPRequiredMixinRedirectSetup
|
||||
|
||||
|
||||
# ? 2FA patch (Admin site protection)
|
||||
admin.site.__class__ = AdminSiteOTPRequiredMixinRedirectSetup
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('collector.urls', namespace='collector')),
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
# JWT AUTH:
|
||||
path(
|
||||
'api/v1/auth/token/',
|
||||
TokenObtainPairView.as_view(),
|
||||
name='token_obtain_pair'
|
||||
),
|
||||
path(
|
||||
'api/v1/auth/token/refresh/',
|
||||
TokenRefreshView.as_view(),
|
||||
name='token_refresh'
|
||||
),
|
||||
path(
|
||||
'api/v1/auth/token/verify/',
|
||||
TokenVerifyView.as_view(),
|
||||
name='token_verify'
|
||||
),
|
||||
path('', include(tf_urls)),
|
||||
path('', include('account.urls', namespace='account'))
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
|
91
logs_collector/templates/theme_swither.html
Normal file
91
logs_collector/templates/theme_swither.html
Normal file
@ -0,0 +1,91 @@
|
||||
<!--Theme switcher icons-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
|
||||
<symbol id="check2" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id="circle-half" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
|
||||
</symbol>
|
||||
<symbol id="moon-stars-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"
|
||||
/>
|
||||
<path
|
||||
d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id="sun-fill" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"
|
||||
/>
|
||||
</symbol>
|
||||
</svg>
|
||||
<!--Theme switcher buttons-->
|
||||
<button
|
||||
class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
|
||||
id="bd-theme"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-label="Toggle theme (auto)"
|
||||
>
|
||||
<svg class="bi my-1 theme-icon-active" width="1em" height="1em">
|
||||
<use href="#circle-half"></use>
|
||||
</svg>
|
||||
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu dropdown-menu-end shadow"
|
||||
aria-labelledby="bd-theme-text"
|
||||
>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="light"
|
||||
aria-pressed="false"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em">
|
||||
<use href="#sun-fill"></use>
|
||||
</svg>
|
||||
Light
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||
<use href="#check2"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center"
|
||||
data-bs-theme-value="dark"
|
||||
aria-pressed="false"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em">
|
||||
<use href="#moon-stars-fill"></use>
|
||||
</svg>
|
||||
Dark
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||
<use href="#check2"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
type="button"
|
||||
class="dropdown-item d-flex align-items-center active"
|
||||
data-bs-theme-value="auto"
|
||||
aria-pressed="true"
|
||||
>
|
||||
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em">
|
||||
<use href="#circle-half"></use>
|
||||
</svg>
|
||||
Auto
|
||||
<svg class="bi ms-auto d-none" width="1em" height="1em">
|
||||
<use href="#check2"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
After Width: | Height: | Size: 3.9 KiB |
42
logs_collector/templates/two_factor/_base.html
Normal file
42
logs_collector/templates/two_factor/_base.html
Normal file
@ -0,0 +1,42 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-bs-theme="auto">
|
||||
<head>
|
||||
{% include 'collector/includes/metalinks.html' %}
|
||||
<title>Collector - {% block title %}{% endblock %}</title>
|
||||
{% block extra_media %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<!-- Two factor auth-->
|
||||
<section>
|
||||
<header>
|
||||
{% block 'navigation' %}{% endblock 'navigation' %}
|
||||
</header>
|
||||
</section>
|
||||
<section>
|
||||
<main>
|
||||
<div
|
||||
class="d-flex min-vh-100 align-items-center py-4 bg-body-tertiary"
|
||||
cz-shortcut-listen="true" >
|
||||
{% block content_wrapper %}
|
||||
<div class="container">
|
||||
<div class="row">{% block content %}{% endblock %}</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
<!-- Theme switcher-->
|
||||
<section>
|
||||
<footer>
|
||||
<div
|
||||
class="dropdown position-fixed bottom-0 end-0 mb-3 me-3 bd-mode-toggle"
|
||||
>
|
||||
{% include 'collector/includes/theme_swither.html' %}
|
||||
</div>
|
||||
</footer>
|
||||
</section>
|
||||
<script src="{% static 'collector/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<script src="{% static 'collector/js/bs.theme.mode.js' %}"></script>
|
||||
</body>
|
||||
</html>
|
11
logs_collector/templates/two_factor/_base_focus.html
Normal file
11
logs_collector/templates/two_factor/_base_focus.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends "two_factor/_base.html" %}
|
||||
|
||||
{% block content_wrapper %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4 offset-md-4">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
14
logs_collector/templates/two_factor/_wizard_actions.html
Normal file
14
logs_collector/templates/two_factor/_wizard_actions.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% if cancel_url %}
|
||||
<a href="{{ cancel_url }}"
|
||||
class="float-right btn btn-link">{% trans "Cancel" %}</a>
|
||||
{% endif %}
|
||||
{% if wizard.steps.prev %}
|
||||
<button name="wizard_goto_step" type="submit"
|
||||
value="{{ wizard.steps.prev }}"
|
||||
class="btn btn-secondary">{% trans "Back" %}</button>
|
||||
{% else %}
|
||||
<button disabled name="" type="button" class="btn">{% trans "Back" %}</button>
|
||||
{% endif %}
|
||||
<button type="submit" class="btn btn-primary">{% trans "Next" %}</button>
|
5
logs_collector/templates/two_factor/_wizard_forms.html
Normal file
5
logs_collector/templates/two_factor/_wizard_forms.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% load crispy_forms_tags %}
|
||||
<div class="mb-3">
|
||||
{{ wizard.management_form }}
|
||||
{{ wizard.form|crispy }}
|
||||
</div>
|
28
logs_collector/templates/two_factor/core/backup_tokens.html
Normal file
28
logs_collector/templates/two_factor/core/backup_tokens.html
Normal file
@ -0,0 +1,28 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Backup Tokens" %}{% endblock %}</h1>
|
||||
<p>{% blocktrans trimmed %}Backup tokens can be used when your primary and backup
|
||||
phone numbers aren't available. The backup tokens below can be used
|
||||
for login verification. If you've used up all your backup tokens, you
|
||||
can generate a new set of backup tokens. Only the backup tokens shown
|
||||
below will be valid.{% endblocktrans %}</p>
|
||||
|
||||
{% if device.token_set.count %}
|
||||
<ul>
|
||||
{% for token in device.token_set.all %}
|
||||
<li>{{ token.token }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p>{% blocktrans %}Print these tokens and keep them somewhere safe.{% endblocktrans %}</p>
|
||||
{% else %}
|
||||
<p>{% trans "You don't have any backup codes yet." %}</p>
|
||||
{% endif %}
|
||||
|
||||
<form method="post">{% csrf_token %}{{ form.as_p }}
|
||||
<a href="{% url 'two_factor:profile'%}"
|
||||
class="float-right btn btn-link">{% trans "Back to Account Security" %}</a>
|
||||
<button class="btn btn-primary" type="submit">{% trans "Generate Tokens" %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
57
logs_collector/templates/two_factor/core/login.html
Normal file
57
logs_collector/templates/two_factor/core/login.html
Normal file
@ -0,0 +1,57 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
{% load two_factor_tags %}
|
||||
|
||||
{% block extra_media %}
|
||||
{{ form.media }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Login" %}{% endblock %}</h1>
|
||||
|
||||
{% if wizard.steps.current == 'auth' %}
|
||||
<p>{% blocktrans %}Enter your credentials.{% endblocktrans %}</p>
|
||||
{% elif wizard.steps.current == 'token' %}
|
||||
<p>{{ device|as_verbose_action }}</p>
|
||||
{% elif wizard.steps.current == 'backup' %}
|
||||
<p>{% blocktrans trimmed %}Use this form for entering backup tokens for logging in.
|
||||
These tokens have been generated for you to print and keep safe. Please
|
||||
enter one of these backup tokens to login to your account.{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% include "two_factor/_wizard_forms.html" %}
|
||||
|
||||
{# hidden submit button to enable [enter] key #}
|
||||
<input type="submit" value="" hidden />
|
||||
|
||||
{% if other_devices %}
|
||||
<p>{% trans "Or, alternatively, use one of your other authentication methods:" %}</p>
|
||||
<p>
|
||||
{% for other in other_devices %}
|
||||
<button name="challenge_device" value="{{ other.persistent_id }}"
|
||||
class="btn btn-secondary btn-block" type="submit">
|
||||
{{ other|as_action }}
|
||||
</button>
|
||||
{% endfor %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% include "two_factor/_wizard_actions.html" %}
|
||||
</form>
|
||||
|
||||
{% block 'backup_tokens' %}
|
||||
{% if backup_tokens %}
|
||||
<hr>
|
||||
<div class="backup_tokens_form">
|
||||
<form action="" method="post">
|
||||
{% csrf_token %}
|
||||
<p>{% trans "As a last resort, you can use a backup token:" %}</p>
|
||||
<p>
|
||||
<button name="wizard_goto_step" type="submit" value="backup"
|
||||
class="btn btn-sm btn-secondary btn-block">{% trans "Use Backup Token" %}</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
20
logs_collector/templates/two_factor/core/otp_required.html
Normal file
20
logs_collector/templates/two_factor/core/otp_required.html
Normal file
@ -0,0 +1,20 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Permission Denied" %}{% endblock %}</h1>
|
||||
|
||||
<p>{% blocktrans trimmed %}The page you requested, enforces users to verify using
|
||||
two-factor authentication for security reasons. You need to enable these
|
||||
security features in order to access this page.{% endblocktrans %}</p>
|
||||
|
||||
<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
|
||||
account. Enable two-factor authentication for enhanced account
|
||||
security.{% endblocktrans %}</p>
|
||||
<p>
|
||||
<a href="javascript:history.go(-1)"
|
||||
class="float-right btn btn-link">{% trans "Go back" %}</a>
|
||||
<a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
|
||||
{% trans "Enable Two-Factor Authentication" %}</a>
|
||||
</p>
|
||||
{% endblock %}
|
24
logs_collector/templates/two_factor/core/phone_register.html
Normal file
24
logs_collector/templates/two_factor/core/phone_register.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Add Backup Phone" %}{% endblock %}</h1>
|
||||
|
||||
{% if wizard.steps.current == 'setup' %}
|
||||
<p>{% blocktrans trimmed %}You'll be adding a backup phone number to your
|
||||
account. This number will be used if your primary method of
|
||||
registration is not available.{% endblocktrans %}</p>
|
||||
{% elif wizard.steps.current == 'validation' %}
|
||||
<p>{% blocktrans trimmed %}We've sent a token to your phone number. Please
|
||||
enter the token you've received.{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% include "two_factor/_wizard_forms.html" %}
|
||||
|
||||
{# hidden submit button to enable [enter] key #}
|
||||
<input type="submit" value="" hidden />
|
||||
|
||||
{% include "two_factor/_wizard_actions.html" %}
|
||||
</form>
|
||||
{% endblock %}
|
64
logs_collector/templates/two_factor/core/setup.html
Normal file
64
logs_collector/templates/two_factor/core/setup.html
Normal file
@ -0,0 +1,64 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block extra_media %}
|
||||
{{ form.media }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Enable Two-Factor Authentication" %}{% endblock %}</h1>
|
||||
{% if wizard.steps.current == 'welcome' %}
|
||||
<p>{% blocktrans trimmed %}You are about to take your account security to the
|
||||
next level. Follow the steps in this wizard to enable two-factor
|
||||
authentication.{% endblocktrans %}</p>
|
||||
{% elif wizard.steps.current == 'method' %}
|
||||
<p>{% blocktrans trimmed %}Please select which authentication method you would
|
||||
like to use.{% endblocktrans %}</p>
|
||||
{% elif wizard.steps.current == 'generator' %}
|
||||
<p>{% blocktrans trimmed %}To start using a token generator, please use your
|
||||
smartphone to scan the QR code below. For example, use Google
|
||||
Authenticator.{% endblocktrans %}</p>
|
||||
<p><img src="{{ QR_URL }}" alt="QR Code" class="bg-white"/></p>
|
||||
<p>{% blocktrans trimmed %}Alternatively you can use the following secret to
|
||||
setup TOTP in your authenticator or password manager manually.{% endblocktrans %}</p>
|
||||
<p>{% translate "TOTP Secret:" %} <a href="{{ otpauth_url }}">{{ secret_key }}</a></p>
|
||||
<p>{% blocktrans %}Then, enter the token generated by the app.{% endblocktrans %}</p>
|
||||
|
||||
{% elif wizard.steps.current == 'sms' %}
|
||||
<p>{% blocktrans trimmed %}Please enter the phone number you wish to receive the
|
||||
text messages on. This number will be validated in the next step.
|
||||
{% endblocktrans %}</p>
|
||||
{% elif wizard.steps.current == 'call' %}
|
||||
<p>{% blocktrans trimmed %}Please enter the phone number you wish to be called on.
|
||||
This number will be validated in the next step. {% endblocktrans %}</p>
|
||||
{% elif wizard.steps.current == 'validation' %}
|
||||
{% if challenge_succeeded %}
|
||||
{% if device.method == 'call' %}
|
||||
<p>{% blocktrans trimmed %}We are calling your phone right now, please enter the
|
||||
digits you hear.{% endblocktrans %}</p>
|
||||
{% elif device.method == 'sms' %}
|
||||
<p>{% blocktrans trimmed %}We sent you a text message, please enter the tokens we
|
||||
sent.{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p class="alert alert-warning" role="alert">{% blocktrans trimmed %}We've
|
||||
encountered an issue with the selected authentication method. Please
|
||||
go back and verify that you entered your information correctly, try
|
||||
again, or use a different authentication method instead. If the issue
|
||||
persists, contact the site administrator.{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
{% elif wizard.steps.current == 'yubikey' %}
|
||||
<p>{% blocktrans trimmed %}To identify and verify your YubiKey, please insert a
|
||||
token in the field below. Your YubiKey will be linked to your
|
||||
account.{% endblocktrans %}</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
{% include "two_factor/_wizard_forms.html" %}
|
||||
|
||||
{# hidden submit button to enable [enter] key #}
|
||||
<input type="submit" value="" hidden />
|
||||
|
||||
{% include "two_factor/_wizard_actions.html" %}
|
||||
</form>
|
||||
{% endblock %}
|
24
logs_collector/templates/two_factor/core/setup_complete.html
Normal file
24
logs_collector/templates/two_factor/core/setup_complete.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Enable Two-Factor Authentication" %}{% endblock %}</h1>
|
||||
|
||||
<p>{% blocktrans trimmed %}Congratulations, you've successfully enabled two-factor
|
||||
authentication.{% endblocktrans %}</p>
|
||||
|
||||
{% if not phone_methods %}
|
||||
<p><a href="{% url 'two_factor:profile' %}"
|
||||
class="btn btn-block btn-secondary">{% trans "Back to Account Security" %}</a></p>
|
||||
{% else %}
|
||||
<p>{% blocktrans trimmed %}However, it might happen that you don't have access to
|
||||
your primary token device. To enable account recovery, add a phone
|
||||
number.{% endblocktrans %}</p>
|
||||
|
||||
<a href="{% url 'two_factor:profile' %}"
|
||||
class="float-right btn btn-link">{% trans "Back to Account Security" %}</a>
|
||||
<p><a href="{% url 'two_factor:phone_create' %}"
|
||||
class="btn btn-success">{% trans "Add Phone Number" %}</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
14
logs_collector/templates/two_factor/profile/disable.html
Normal file
14
logs_collector/templates/two_factor/profile/disable.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends "two_factor/_base_focus.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Disable Two-factor Authentication" %}{% endblock %}</h1>
|
||||
<p>{% blocktrans trimmed %}You are about to disable two-factor authentication. This
|
||||
weakens your account security, are you sure?{% endblocktrans %}</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<table>{{ form.as_table }}</table>
|
||||
<button class="btn btn-danger"
|
||||
type="submit">{% trans "Disable" %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
62
logs_collector/templates/two_factor/profile/profile.html
Normal file
62
logs_collector/templates/two_factor/profile/profile.html
Normal file
@ -0,0 +1,62 @@
|
||||
{% extends "two_factor/_base.html" %}
|
||||
{% load i18n %}
|
||||
{% load two_factor_tags %}
|
||||
|
||||
{% block 'navigation' %}
|
||||
{% include 'collector/includes/navigation.html' %}
|
||||
{% endblock 'navigation' %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% block title %}{% trans "Account Security" %}{% endblock %}</h1>
|
||||
|
||||
{% if default_device %}
|
||||
<p>{% blocktrans with primary=default_device|as_action %}Primary method: {{ primary }}{% endblocktrans %}</p>
|
||||
|
||||
{% if available_phone_methods %}
|
||||
<h2>{% trans "Backup Phone Numbers" %}</h2>
|
||||
<p>{% blocktrans trimmed %}If your primary method is not available, we are able to
|
||||
send backup tokens to the phone numbers listed below.{% endblocktrans %}</p>
|
||||
<ul>
|
||||
{% for phone in backup_phones %}
|
||||
<li>
|
||||
{{ phone|as_action }}
|
||||
<form method="post" action="{% url 'two_factor:phone_delete' phone.id %}"
|
||||
onsubmit="return confirm({% trans 'Are you sure?' %})">
|
||||
{% csrf_token %}
|
||||
<button class="btn btn-sm btn-warning"
|
||||
type="submit">{% trans "Unregister" %}</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p><a href="{% url 'two_factor:phone_create' %}"
|
||||
class="btn btn-info">{% trans "Add Phone Number" %}</a></p>
|
||||
{% endif %}
|
||||
|
||||
<h2>{% trans "Backup Tokens" %}</h2>
|
||||
<p>
|
||||
{% blocktrans trimmed %}If you don't have any device with you, you can access
|
||||
your account using backup tokens.{% endblocktrans %}
|
||||
{% blocktrans trimmed count counter=backup_tokens %}
|
||||
You have only one backup token remaining.
|
||||
{% plural %}
|
||||
You have {{ counter }} backup tokens remaining.
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p><a href="{% url 'two_factor:backup_tokens' %}"
|
||||
class="btn btn-info">{% trans "Show Codes" %}</a></p>
|
||||
|
||||
<h3>{% trans "Disable Two-Factor Authentication" %}</h3>
|
||||
<p>{% blocktrans trimmed %}However we strongly discourage you to do so, you can
|
||||
also disable two-factor authentication for your account.{% endblocktrans %}</p>
|
||||
<p><a class="btn btn-secondary" href="{% url 'two_factor:disable' %}">
|
||||
{% trans "Disable Two-Factor Authentication" %}</a></p>
|
||||
{% else %}
|
||||
<p>{% blocktrans trimmed %}Two-factor authentication is not enabled for your
|
||||
account. Enable two-factor authentication for enhanced account
|
||||
security.{% endblocktrans %}</p>
|
||||
<p><a href="{% url 'two_factor:setup' %}" class="btn btn-primary">
|
||||
{% trans "Enable Two-Factor Authentication" %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -0,0 +1,7 @@
|
||||
{% load i18n %}<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<Response>
|
||||
<Gather timeout="15" numDigits="1" finishOnKey="">
|
||||
<Say language="{{ locale }}">{% blocktrans %}Hi, this is {{ site_name }} calling. Press any key to continue.{% endblocktrans %}</Say>
|
||||
</Gather>
|
||||
<Say language="{{ locale }}">{% trans "You didn’t press any keys. Good bye." %}</Say>
|
||||
</Response>
|
@ -0,0 +1,5 @@
|
||||
{% load i18n %}
|
||||
{% blocktrans trimmed %}
|
||||
Your OTP token is {{ token }}
|
||||
{% endblocktrans %}
|
||||
|
12
logs_collector/templates/two_factor/twilio/token.xml
Normal file
12
logs_collector/templates/two_factor/twilio/token.xml
Normal file
@ -0,0 +1,12 @@
|
||||
{% load i18n %}<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<Response>
|
||||
<Say language="{{ locale }}">{% trans "Your token is:" %}</Say>
|
||||
<Pause>
|
||||
{% for digit in token %} <Say language="{{ locale }}">{{ digit }}</Say>
|
||||
<Pause>
|
||||
{% endfor %} <Say language="{{ locale }}">{% trans "Repeat:" %}</Say>
|
||||
<Pause>
|
||||
{% for digit in token %} <Say language="{{ locale }}">{{ digit }}</Say>
|
||||
<Pause>
|
||||
{% endfor %} <Say language="{{ locale }}">{% trans "Good bye." %}</Say>
|
||||
</Response>
|
134
poetry.lock
generated
134
poetry.lock
generated
@ -35,6 +35,17 @@ docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-
|
||||
tests = ["attrs[tests-no-zope]", "zope-interface"]
|
||||
tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crispy-bootstrap5"
|
||||
version = "0.7"
|
||||
@ -128,6 +139,82 @@ files = [
|
||||
[package.dependencies]
|
||||
Django = ">=3.2"
|
||||
|
||||
[[package]]
|
||||
name = "django-formtools"
|
||||
version = "2.4.1"
|
||||
description = "A set of high-level abstractions for Django forms"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "django-formtools-2.4.1.tar.gz", hash = "sha256:21f8d5dac737f1e636fa8a0a10969c1c32f525a6dfa27c29592827ba70d9643a"},
|
||||
{file = "django_formtools-2.4.1-py3-none-any.whl", hash = "sha256:49ea8a64ddef4728a558bf5f8f622c0f4053b979edcf193bf00dd80432ab2f12"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=3.2"
|
||||
|
||||
[[package]]
|
||||
name = "django-otp"
|
||||
version = "1.2.2"
|
||||
description = "A pluggable framework for adding two-factor authentication to Django using one-time passwords."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "django_otp-1.2.2-py3-none-any.whl", hash = "sha256:90765d5dac238a719f9550ac05681dd6307f513a81a10b6adb879b4abc6bc1a3"},
|
||||
{file = "django_otp-1.2.2.tar.gz", hash = "sha256:007a6354dabb3a1a54574bf73abf045ebbde0bb8734a38e2ed7845ba450f345e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
django = ">=3.2"
|
||||
|
||||
[package.extras]
|
||||
qrcode = ["qrcode"]
|
||||
|
||||
[[package]]
|
||||
name = "django-phonenumber-field"
|
||||
version = "6.4.0"
|
||||
description = "An international phone number field for django models."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "django-phonenumber-field-6.4.0.tar.gz", hash = "sha256:72a3e7a3e7493bf2a12c07a3bc77ce89813acc16592bf04d0eee3b5a452097ed"},
|
||||
{file = "django_phonenumber_field-6.4.0-py3-none-any.whl", hash = "sha256:a31b4f05ac0ff898661516c84940f83adb5cdcf0ae4b9b1d31bb8ad3ff345b58"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=3.2"
|
||||
|
||||
[package.extras]
|
||||
phonenumbers = ["phonenumbers (>=7.0.2)"]
|
||||
phonenumberslite = ["phonenumberslite (>=7.0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "django-two-factor-auth"
|
||||
version = "1.15.3"
|
||||
description = "Complete Two-Factor Authentication for Django"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "django-two-factor-auth-1.15.3.tar.gz", hash = "sha256:311b7f0c5ee47ae5c3734f7810f90c9390b3aef556f58a767b0d80d6b54013fb"},
|
||||
{file = "django_two_factor_auth-1.15.3-py3-none-any.whl", hash = "sha256:a5752732225304ba0461ec6f5347def517bdde98685e90bb7879aa066c91f3a4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
Django = ">=3.2"
|
||||
django-formtools = "*"
|
||||
django-otp = ">=0.8.0"
|
||||
django-phonenumber-field = ">=1.1.0,<7"
|
||||
phonenumberslite = {version = ">=7.0.9,<8.99", optional = true, markers = "extra == \"phonenumberslite\""}
|
||||
qrcode = ">=4.0.0,<7.99"
|
||||
|
||||
[package.extras]
|
||||
call = ["twilio (>=6.0)"]
|
||||
phonenumbers = ["phonenumbers (>=7.0.9,<8.99)"]
|
||||
phonenumberslite = ["phonenumberslite (>=7.0.9,<8.99)"]
|
||||
sms = ["twilio (>=6.0)"]
|
||||
webauthn = ["pydantic (>=1.9.0,<1.99)", "webauthn (>=1.6.0,<1.99)"]
|
||||
yubikey = ["django-otp-yubikey"]
|
||||
|
||||
[[package]]
|
||||
name = "djangorestframework"
|
||||
version = "3.14.0"
|
||||
@ -278,6 +365,17 @@ files = [
|
||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phonenumberslite"
|
||||
version = "8.13.18"
|
||||
description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "phonenumberslite-8.13.18-py2.py3-none-any.whl", hash = "sha256:40cef03b24f2bc5711fed2b53b72770ff58f6b7dbfff749822c91078d6e82481"},
|
||||
{file = "phonenumberslite-8.13.18.tar.gz", hash = "sha256:a321f0decf3e4e080f005fda3fba5a791d9d14a3ca217974345ff452923c31e2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pycodestyle"
|
||||
version = "2.10.0"
|
||||
@ -317,6 +415,17 @@ dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pyte
|
||||
docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"]
|
||||
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pypng"
|
||||
version = "0.20220715.0"
|
||||
description = "Pure Python library for saving and loading PNG images"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c"},
|
||||
{file = "pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2023.3"
|
||||
@ -377,6 +486,29 @@ files = [
|
||||
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qrcode"
|
||||
version = "7.4.2"
|
||||
description = "QR Code image generator"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "qrcode-7.4.2-py3-none-any.whl", hash = "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a"},
|
||||
{file = "qrcode-7.4.2.tar.gz", hash = "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
pypng = "*"
|
||||
typing-extensions = "*"
|
||||
|
||||
[package.extras]
|
||||
all = ["pillow (>=9.1.0)", "pytest", "pytest-cov", "tox", "zest.releaser[recommended]"]
|
||||
dev = ["pytest", "pytest-cov", "tox"]
|
||||
maintainer = ["zest.releaser[recommended]"]
|
||||
pil = ["pillow (>=9.1.0)"]
|
||||
test = ["coverage", "pytest"]
|
||||
|
||||
[[package]]
|
||||
name = "referencing"
|
||||
version = "0.30.2"
|
||||
@ -550,4 +682,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "fd9080bb86f6f84b684d431507eaaf41306e13fbb4f88f07f2a7c82f8823b728"
|
||||
content-hash = "69aa50d072f03697f4d11a333694d01f2528e433185216e3dc8d32b2debe7432"
|
||||
|
@ -18,6 +18,7 @@ crispy-bootstrap5 = "^0.7"
|
||||
markdown = "^3.4.4"
|
||||
django-filter = "^23.2"
|
||||
drf-spectacular = "^0.26.4"
|
||||
django-two-factor-auth = {extras = ["phonenumberslite"], version = "^1.15.3"}
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
flake8 = "^6.0.0"
|
||||
|
Loading…
Reference in New Issue
Block a user