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>
|
<!DOCTYPE html>
|
||||||
<html lang="en" data-bs-theme="auto">
|
<html lang="en" data-bs-theme="auto">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
{% include 'collector/includes/metalinks.html' %}
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<title>{% block title %}{% endblock title %}</title>
|
||||||
<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 %}
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header>
|
<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 %}
|
{% load collector_extras %}
|
||||||
{% get_platforms as platforms %}
|
{% 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">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a
|
<a
|
||||||
@ -114,7 +99,13 @@
|
|||||||
</li>
|
</li>
|
||||||
<li><button class="dropdown-item" type="button"><i class="bi bi-gear"></i> Settings</button></li>
|
<li><button class="dropdown-item" type="button"><i class="bi bi-gear"></i> Settings</button></li>
|
||||||
<li><hr class="dropdown-divider" /></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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@ -123,7 +114,7 @@
|
|||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<a
|
<a
|
||||||
type="button"
|
type="button"
|
||||||
href="{% url 'admin:index' %}"
|
href="{% url 'two_factor:login' %}"
|
||||||
class="btn btn-outline-secondary"
|
class="btn btn-outline-secondary"
|
||||||
>
|
>
|
||||||
<i class="bi bi-box-arrow-in-right"></i></i> Login
|
<i class="bi bi-box-arrow-in-right"></i></i> Login
|
||||||
@ -139,42 +130,7 @@
|
|||||||
<!-- Theme switcher-->
|
<!-- Theme switcher-->
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<div class="dropdown bd-mode-toggle">
|
<div class="dropdown bd-mode-toggle">
|
||||||
<button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
|
{% include 'collector/includes/theme_swither.html' %}
|
||||||
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>
|
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</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' %}
|
{% extends 'collector/base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load collector_extras %}
|
{% load collector_extras %}
|
||||||
{% block title %}<title>{{ title }}</title>{% endblock title %}
|
{% block title %} {{ title }} {% endblock title %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div class="container mt-3">
|
<div class="container mt-3">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends 'collector/base.html' %}
|
{% extends 'collector/base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
{% block title %}<title>{{ title }}</title>{% endblock title %}
|
{% block title %} {{ title }} {% endblock title %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div class="container mt-3">
|
<div class="container mt-3">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{% extends 'collector/base.html' %}
|
{% extends 'collector/base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load collector_extras %}
|
{% load collector_extras %}
|
||||||
{% block title %}<title>{{ title }}</title>{% endblock title %}
|
{% block title %} {{ title }} {% endblock title %}
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div class="container mt-3">
|
<div class="container mt-3">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
@ -15,6 +15,8 @@ from rest_framework import filters
|
|||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
|
||||||
|
from two_factor.views import OTPRequiredMixin
|
||||||
|
|
||||||
from .models import Archive, Ticket, Platform
|
from .models import Archive, Ticket, Platform
|
||||||
from .forms import TicketForm
|
from .forms import TicketForm
|
||||||
from .filters import ArchiveFilter, TicketFilter
|
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
|
model = Archive
|
||||||
slug_field = 'file'
|
slug_field = 'file'
|
||||||
slug_url_kwarg = 'path'
|
slug_url_kwarg = 'path'
|
||||||
@ -67,7 +73,7 @@ class UpdateTicket(LoginRequiredMixin, PageTitleViewMixin, generic.UpdateView):
|
|||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class ListAllTickets(PageTitleViewMixin, generic.ListView):
|
class ListAllTickets(LoginRequiredMixin, PageTitleViewMixin, generic.ListView):
|
||||||
model = Ticket
|
model = Ticket
|
||||||
template_name = 'collector/tickets.html'
|
template_name = 'collector/tickets.html'
|
||||||
context_object_name = 'tickets'
|
context_object_name = 'tickets'
|
||||||
|
@ -36,12 +36,18 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'collector.apps.CollectorConfig', # main app
|
'collector.apps.CollectorConfig', # main app
|
||||||
|
'account.apps.AccountConfig', # account app
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework_simplejwt',
|
'rest_framework_simplejwt',
|
||||||
'django_filters',
|
'django_filters',
|
||||||
'drf_spectacular',
|
'drf_spectacular',
|
||||||
"crispy_forms",
|
"crispy_forms",
|
||||||
"crispy_bootstrap5",
|
"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
|
'django_cleanup.apps.CleanupConfig', # required bottom
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -51,6 +57,7 @@ MIDDLEWARE = [
|
|||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django_otp.middleware.OTPMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
]
|
]
|
||||||
@ -60,7 +67,7 @@ ROOT_URLCONF = 'logs_collector.urls'
|
|||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [],
|
'DIRS': [BASE_DIR / 'templates'],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
@ -216,3 +223,7 @@ SIMPLE_JWT = {
|
|||||||
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer", # noqa:E501
|
"SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer", # noqa:E501
|
||||||
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer", # 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.conf.urls.static import static
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
from rest_framework_simplejwt.views import (
|
|
||||||
TokenObtainPairView,
|
|
||||||
TokenRefreshView,
|
|
||||||
TokenVerifyView
|
|
||||||
)
|
|
||||||
|
|
||||||
from drf_spectacular.views import (
|
from drf_spectacular.views import (
|
||||||
SpectacularAPIView,
|
SpectacularAPIView,
|
||||||
SpectacularRedocView,
|
SpectacularRedocView,
|
||||||
SpectacularSwaggerView
|
SpectacularSwaggerView
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from two_factor.urls import urlpatterns as tf_urls
|
||||||
|
|
||||||
from logs_collector import settings
|
from logs_collector import settings
|
||||||
|
from account.utils import AdminSiteOTPRequiredMixinRedirectSetup
|
||||||
|
|
||||||
|
|
||||||
|
# ? 2FA patch (Admin site protection)
|
||||||
|
admin.site.__class__ = AdminSiteOTPRequiredMixinRedirectSetup
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('', include('collector.urls', namespace='collector')),
|
path('', include('collector.urls', namespace='collector')),
|
||||||
]
|
path('', include(tf_urls)),
|
||||||
|
path('', include('account.urls', namespace='account'))
|
||||||
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'
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += [
|
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 = ["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]"]
|
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]]
|
[[package]]
|
||||||
name = "crispy-bootstrap5"
|
name = "crispy-bootstrap5"
|
||||||
version = "0.7"
|
version = "0.7"
|
||||||
@ -128,6 +139,82 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
Django = ">=3.2"
|
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]]
|
[[package]]
|
||||||
name = "djangorestframework"
|
name = "djangorestframework"
|
||||||
version = "3.14.0"
|
version = "3.14.0"
|
||||||
@ -278,6 +365,17 @@ files = [
|
|||||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
{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]]
|
[[package]]
|
||||||
name = "pycodestyle"
|
name = "pycodestyle"
|
||||||
version = "2.10.0"
|
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"]
|
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)"]
|
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]]
|
[[package]]
|
||||||
name = "pytz"
|
name = "pytz"
|
||||||
version = "2023.3"
|
version = "2023.3"
|
||||||
@ -377,6 +486,29 @@ files = [
|
|||||||
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
|
{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]]
|
[[package]]
|
||||||
name = "referencing"
|
name = "referencing"
|
||||||
version = "0.30.2"
|
version = "0.30.2"
|
||||||
@ -550,4 +682,4 @@ files = [
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "fd9080bb86f6f84b684d431507eaaf41306e13fbb4f88f07f2a7c82f8823b728"
|
content-hash = "69aa50d072f03697f4d11a333694d01f2528e433185216e3dc8d32b2debe7432"
|
||||||
|
@ -18,6 +18,7 @@ crispy-bootstrap5 = "^0.7"
|
|||||||
markdown = "^3.4.4"
|
markdown = "^3.4.4"
|
||||||
django-filter = "^23.2"
|
django-filter = "^23.2"
|
||||||
drf-spectacular = "^0.26.4"
|
drf-spectacular = "^0.26.4"
|
||||||
|
django-two-factor-auth = {extras = ["phonenumberslite"], version = "^1.15.3"}
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
flake8 = "^6.0.0"
|
flake8 = "^6.0.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user