Finally got all the OTP stuff working

Finalized dbsettings views
Easter egg for missing backend pages
This commit is contained in:
Kumi 2020-04-15 22:19:03 +02:00
parent d74a4c1b8b
commit 7708128255
27 changed files with 610 additions and 87 deletions

View file

@ -9,6 +9,9 @@ class BaseOTPProvider:
def get_logo(self):
return ""
def __str__(self):
return self.get_name
@property
def is_active(self):
'''Returns True if the provider is properly configured and ready to use.'''

1
core/forms/__init__.py Normal file
View file

@ -0,0 +1 @@
from core.forms.auth import *

16
core/forms/auth.py Normal file
View file

@ -0,0 +1,16 @@
from django.forms import Form, EmailField, CharField, PasswordInput, ChoiceField
from core.helpers.otp import get_otp_choices
class LoginForm(Form):
email = EmailField()
password = CharField(widget=PasswordInput)
class OTPSelectorForm(Form):
def __init__(self, *args, **kwargs):
otp_choices = kwargs.pop('otp_choices', [])
super(OTPSelectorForm, self).__init__(*args, **kwargs)
self.fields['provider'] = ChoiceField(choices=otp_choices)
class OTPVerificationForm(Form):
token = CharField()

25
core/helpers/otp.py Normal file
View file

@ -0,0 +1,25 @@
from core.models import OTPUser
from core.modules.otp import providers
def get_user_otps(user):
try:
all_otps = OTPUser.objects.filter(user=user)
except:
return {}
user_providers = [otp.provider for otp in all_otps]
active_providers = {}
for name, provider in providers.items():
if name in user_providers:
active_providers[name] = provider
return active_providers
def get_otp_by_name(name):
for pname, provider in providers.items():
if pname == name:
return provider
def get_otp_choices(user):
return [(name, provider) for name, provider in get_user_otps(user).items()]

View file

@ -1,2 +1,3 @@
from core.models.files import *
from core.models.profiles import *
from core.models.profiles import *
from core.models.otp import *

13
core/models/otp.py Normal file
View file

@ -0,0 +1,13 @@
from django.db.models import Model, ForeignKey, CharField, DateTimeField, UUIDField, CASCADE
from django.contrib.auth import get_user_model
from uuid import uuid4
class OTPUser(Model):
user = ForeignKey(get_user_model(), CASCADE)
provider = CharField(max_length=255)
class LoginSession(Model):
uuid = UUIDField(default=uuid4, primary_key=True)
user = ForeignKey(get_user_model(), CASCADE)
creation = DateTimeField(auto_now_add=True)

View file

@ -2,12 +2,12 @@ import importlib
from django.conf import settings
providers = []
providers = {}
for module in settings.EXPEPHALON_MODULES:
try:
moo = importlib.import_module(f"{module}.otp")
for provider in moo.OTPPROVIDERS:
providers.append(provider)
for name, provider in moo.OTPPROVIDERS.items():
providers[name] = provider
except (AttributeError, ModuleNotFoundError):
continue

View file

@ -3,7 +3,16 @@ import importlib
from django.conf import settings
from django.urls import path
URLPATTERNS = []
from core.views import DashboardView, LoginView, OTPSelectorView, LogoutView, OTPValidatorView, BackendNotImplementedView
URLPATTERNS = [
path('login/', LoginView.as_view(), name="login"),
path('login/otp/select/', OTPSelectorView.as_view(), name="otpselector"),
path('login/otp/validate/', OTPValidatorView.as_view(), name="otpvalidator"),
path('logout/', LogoutView.as_view(), name="logout"),
path('admin/', DashboardView.as_view(), name="dashboard"),
path('admin/oops/', BackendNotImplementedView.as_view(), name="backendni")
]
for module in settings.EXPEPHALON_MODULES:
try:
@ -13,6 +22,11 @@ for module in settings.EXPEPHALON_MODULES:
except (AttributeError, ModuleNotFoundError):
pass
if "dbsettings" in settings.INSTALLED_APPS:
from core.views import DBSettingsListView
URLPATTERNS.append(path("admin/dbsettings/", DBSettingsListView.as_view(), name="dbsettings"))
try:
from core.views import DBSettingsListView, DBSettingsEditView, DBSettingsDeleteView, DBSettingsCreateView
URLPATTERNS.append(path("admin/dbsettings/", DBSettingsListView.as_view(), name="dbsettings"))
URLPATTERNS.append(path("admin/dbsettings/<pk>/delete/", DBSettingsDeleteView.as_view(), name="dbsettings_delete"))
URLPATTERNS.append(path("admin/dbsettings/<pk>/edit/", DBSettingsEditView.as_view(), name="dbsettings_edit"))
URLPATTERNS.append(path("admin/dbsettings/create/", DBSettingsCreateView.as_view(), name="dbsettings_create"))
except:
pass

View file

@ -6,7 +6,7 @@ from django.conf import settings
dashboard_section = NavSection("Dashboard", "")
dashboard_item = NavItem("Dashboard", "fa-rocket", "backend")
dashboard_item = NavItem("Dashboard", "fa-rocket", "dashboard")
dashboard_section.add_item(dashboard_item)
@ -16,10 +16,10 @@ navigations["backend_main"].add_section(dashboard_section)
clients_section = NavSection("Clients", "")
client_list_item = NavItem("List Clients", "fa-user-tag", "backend")
client_add_item = NavItem("Add Client", "fa-user-edit", "backend")
client_groups_item = NavItem("Client Groups", "fa-users", "backend")
client_leads_item = NavItem("Leads", "fa-blender-phone", "backend")
client_list_item = NavItem("List Clients", "fa-user-tag", "backendni")
client_add_item = NavItem("Add Client", "fa-user-edit", "backendni")
client_groups_item = NavItem("Client Groups", "fa-users", "backendni")
client_leads_item = NavItem("Leads", "fa-blender-phone", "backendni")
clients_section.add_item(client_list_item)
clients_section.add_item(client_add_item)
@ -32,8 +32,8 @@ navigations["backend_main"].add_section(clients_section)
quotes_section = NavSection("Quotes", "")
quote_list_item = NavItem("List Quotes", "fa-file-invoice-dollar", "backend")
quote_create_item = NavItem("Create Quote", "fa-plus-square", "backend")
quote_list_item = NavItem("List Quotes", "fa-file-invoice-dollar", "backendni")
quote_create_item = NavItem("Create Quote", "fa-plus-square", "backendni")
quotes_section.add_item(quote_list_item)
quotes_section.add_item(quote_create_item)
@ -44,11 +44,11 @@ navigations["backend_main"].add_section(quotes_section)
billing_section = NavSection("Billing", "")
invoice_list_item = NavItem("List Invoices", "fa-file-invoice-dollar", "backend")
invoice_create_item = NavItem("Create Invoice", "fa-plus-square", "backend")
billable_list_item = NavItem("List Billable Items", "fa-hand-holding-usd", "backend")
billable_create_item = NavItem("Create Billable Item", "fa-plus-square", "backend")
list_transaction_item = NavItem("Transaction List", "fa-funnel-dollar", "backend")
invoice_list_item = NavItem("List Invoices", "fa-file-invoice-dollar", "backendni")
invoice_create_item = NavItem("Create Invoice", "fa-plus-square", "backendni")
billable_list_item = NavItem("List Billable Items", "fa-hand-holding-usd", "backendni")
billable_create_item = NavItem("Create Billable Item", "fa-plus-square", "backendni")
list_transaction_item = NavItem("Transaction List", "fa-funnel-dollar", "backendni")
billing_section.add_item(invoice_list_item)
billing_section.add_item(invoice_create_item)
@ -62,9 +62,9 @@ navigations["backend_main"].add_section(billing_section)
support_section = NavSection("Support", "")
ticket_view_item = NavItem("View Tickets", "fa-life-ring", "backend")
ticket_add_item = NavItem("Add Ticket", "fa-plus-square", "backend")
conversation_add_item = NavItem("Add Conversation", "fa-comments", "backend")
ticket_view_item = NavItem("View Tickets", "fa-life-ring", "backendni")
ticket_add_item = NavItem("Add Ticket", "fa-plus-square", "backendni")
conversation_add_item = NavItem("Add Conversation", "fa-comments", "backendni")
support_section.add_item(ticket_view_item)
support_section.add_item(ticket_add_item)
@ -76,8 +76,8 @@ navigations["backend_main"].add_section(support_section)
reports_section = NavSection("Reports", "")
report_period_item = NavItem("Income by period", "fa-chart-bar", "backend")
report_forecast_item = NavItem("Income Forecast", "fa-chart-area", "backend")
report_period_item = NavItem("Income by period", "fa-chart-bar", "backendni")
report_forecast_item = NavItem("Income Forecast", "fa-chart-area", "backendni")
reports_section.add_item(report_period_item)
reports_section.add_item(report_forecast_item)
@ -88,14 +88,14 @@ navigations["backend_main"].add_section(reports_section)
administration_section = NavSection("Administration", "")
user_administration_item = NavItem("Administrator Users", "fa-users-cog", "backend")
brand_administration_item = NavItem("Brands", "fa-code-branch", "backend")
sms_administration_item = NavItem("SMS Gateway", "fa-sms", "backend")
otp_administration_item = NavItem("Two-Factor Authentication", "fa-id-badge", "backend")
backup_administration_item = NavItem("Backups", "fa-shield-alt", "backend")
product_administration_item = NavItem("Products", "fa-cube", "backend")
pgroup_administration_item = NavItem("Product Groups", "fa-cubes", "backend")
payment_administration_item = NavItem("Payment Gateways", "fa-credit-card", "backend")
user_administration_item = NavItem("Administrator Users", "fa-users-cog", "backendni")
brand_administration_item = NavItem("Brands", "fa-code-branch", "backendni")
sms_administration_item = NavItem("SMS Gateway", "fa-sms", "backendni")
otp_administration_item = NavItem("Two-Factor Authentication", "fa-id-badge", "backendni")
backup_administration_item = NavItem("Backups", "fa-shield-alt", "backendni")
product_administration_item = NavItem("Products", "fa-cube", "backendni")
pgroup_administration_item = NavItem("Product Groups", "fa-cubes", "backendni")
payment_administration_item = NavItem("Payment Gateways", "fa-credit-card", "backendni")
dbsettings_item = NavItem("Database Settings", "fa-database", "dbsettings")
administration_section.add_item(user_administration_item)

View file

@ -1,10 +1 @@
from django.urls import path, include
from core.views import DashboardView
from core.modules.urls import URLPATTERNS
import importlib
urlpatterns = [
path('admin/', DashboardView.as_view(), name="backend"),
] + URLPATTERNS
from core.modules.urls import URLPATTERNS as urlpatterns

View file

@ -1,7 +1,10 @@
from django.shortcuts import render
from django.views.generic import TemplateView, ListView
from django.views.generic import TemplateView
from django.conf import settings
from core.views.dbsettings import *
from core.views.auth import *
# Create your views here.
class IndexView(TemplateView):
@ -10,12 +13,5 @@ class IndexView(TemplateView):
class DashboardView(TemplateView):
template_name = f"{settings.EXPEPHALON_BACKEND}/index.html"
try:
from dbsettings.models import Setting
class DBSettingsListView(ListView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings.html"
model = Setting
except ModuleNotFoundError:
pass
class BackendNotImplementedView(TemplateView):
template_name = f"{settings.EXPEPHALON_BACKEND}/notimplemented.html"

113
core/views/auth.py Normal file
View file

@ -0,0 +1,113 @@
from django.conf import settings
from django.views.generic import FormView, View
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import redirect
from django.core.exceptions import PermissionDenied
from django.contrib import messages
from core.forms import LoginForm, OTPSelectorForm, OTPVerificationForm
from core.models.otp import LoginSession
from core.helpers.otp import get_user_otps, get_otp_choices, get_otp_by_name
class LoginView(FormView):
template_name = f"{settings.EXPEPHALON_BACKEND}/login.html"
form_class = LoginForm
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect(request.GET.get("next", "dashboard"))
return super().get(request, *args, **kwargs)
def form_valid(self, form):
user = authenticate(username=form.cleaned_data['email'],password=form.cleaned_data['password'])
if user:
if not get_user_otps(user):
login(self.request, user)
return redirect("dashboard")
session = LoginSession.objects.create(user=user)
self.request.session["otpsession"] = str(session.uuid)
self.request.session["next"] = self.request.GET.get("next", "dashboard")
return redirect("otpselector")
return super().form_invalid(form)
class OTPSelectorView(FormView):
template_name = f"{settings.EXPEPHALON_BACKEND}/otp_selector.html"
form_class = OTPSelectorForm
def clean_session(self):
for key in ("otpsession", "otpprovider", "next"):
try:
del self.request.session[key]
except:
pass
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
try:
assert self.request.session["otpsession"]
except:
raise PermissionDenied()
user = LoginSession.objects.get(uuid=self.request.session["otpsession"]).user
kwargs["otp_choices"] = get_otp_choices(user)
return kwargs
def form_valid(self, form):
self.request.session["otpprovider"] = form.cleaned_data["provider"]
return redirect("otpvalidator")
def form_invalid(self, form):
self.clean_session()
return redirect("login")
class OTPValidatorView(FormView):
template_name = f"{settings.EXPEPHALON_BACKEND}/otp_verifier.html"
form_class = OTPVerificationForm
def clean_session(self):
for key in ("otpsession", "otpprovider", "next"):
try:
del self.request.session[key]
except:
pass
def validate_session(self, request):
try:
assert request.session["otpsession"]
assert request.session["otpprovider"]
user = LoginSession.objects.get(uuid=request.session["otpsession"]).user
assert request.session["otpprovider"] in get_user_otps(user).keys()
provider = get_otp_by_name(request.session["otpprovider"])()
return user, provider
except:
self.clean_session()
raise PermissionDenied()
def get(self, request, *args, **kwargs):
user, provider = self.validate_session(request)
response = provider.start_authentication(user)
messages.info(request, response)
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.validate_session(request)
return super().post(request, *args, **kwargs)
def form_invalid(self, form):
self.clean_session()
return redirect("login")
def form_valid(self, form):
user, provider = self.validate_session(self.request)
if provider.validate_token(user, form.cleaned_data["token"]):
login(self.request, user)
ret = redirect(self.request.session.get("next", "dashboard"))
self.clean_session()
return ret
self.clean_session()
messages.error(self.request, "Incorrect token entered. Please try again. If the issue persists, contact support to regain access to your account.")
return redirect("login")
class LogoutView(View):
def get(self, request, *args, **kwargs):
logout(request)
return redirect("login")

30
core/views/dbsettings.py Normal file
View file

@ -0,0 +1,30 @@
from django.conf import settings
from django.views.generic import ListView, UpdateView, DeleteView, CreateView
from django.urls import reverse_lazy
try:
from dbsettings.models import Setting
class DBSettingsListView(ListView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings.html"
model = Setting
class DBSettingsEditView(UpdateView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings_update.html"
model = Setting
success_url = reverse_lazy("dbsettings")
fields = ["key", "value"]
class DBSettingsDeleteView(DeleteView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings_delete.html"
model = Setting
success_url = reverse_lazy("dbsettings")
class DBSettingsCreateView(CreateView):
template_name = f"{settings.EXPEPHALON_BACKEND}/dbsettings_create.html"
model = Setting
success_url = reverse_lazy("dbsettings")
fields = ["key", "value"]
except ModuleNotFoundError:
pass

View file

@ -16,6 +16,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'polymorphic',
'phonenumber_field',
'bootstrap4',
'core',
'dbsettings',
] + EXPEPHALON_MODULES

View file

@ -5,3 +5,4 @@ boto3
Pillow
django-polymorphic
django-phonenumber-field[phonenumbers]
django-bootstrap4

View file

@ -37,4 +37,4 @@ class SMSOTP(BaseOTPProvider):
except OTPToken.DoesNotExist:
return False
OTPPROVIDERS = [SMSOTP]
OTPPROVIDERS = {"smsotp": SMSOTP}

@ -1 +0,0 @@
Subproject commit 4e6402443679e0a9d12c7401ac8783ef4646657f

View file

@ -0,0 +1,46 @@
{% load static %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="Content-Language" content="en">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Login - ArchitectUI HTML Bootstrap 4 Dashboard Template</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, shrink-to-fit=no"
/>
<meta name="description" content="ArchitectUI HTML Bootstrap 4 Dashboard Template">
<!-- Disable tap highlight on IE -->
<meta name="msapplication-tap-highlight" content="no">
<link href="{% static "backend/css/main.css" %}" rel="stylesheet"></head>
<body>
<div class="app-container app-theme-white body-tabs-shadow">
<div class="app-container">
<div class="h-100">
<div class="h-100 no-gutters row">
<div class="d-none d-lg-block col-lg-4">
<div class="slider-light">
<div class="slick-slider">
<div>
<div class="position-relative h-100 d-flex justify-content-center align-items-center bg-plum-plate" tabindex="-1">
<div class="slide-img-bg" style="background-image: url('assets/images/originals/city.jpg');"></div>
<div class="slider-content"><h3>Perfect Balance</h3>
<p>ArchitectUI is like a dream. Some think it's too good to be true! Extensive collection of unified React Boostrap Components and Elements.</p></div>
</div>
</div>
</div>
</div>
</div>
<div class="h-100 d-flex bg-white justify-content-center align-items-center col-md-12 col-lg-8">
{% block content %}{% endblock %}
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="{% static "backend/scripts/main.js" %}"></script></body>
</html>

View file

@ -102,16 +102,16 @@
<h6 tabindex="-1" class="dropdown-header">Header</h6>
<button type="button" tabindex="0" class="dropdown-item">Actions</button>
<div tabindex="-1" class="dropdown-divider"></div>
<button type="button" tabindex="0" class="dropdown-item">Dividers</button>
<a href="{% url "logout" %}" type="button" tabindex="0" class="dropdown-item">Logout</a>
</div>
</div>
</div>
<div class="widget-content-left ml-3 header-user-info">
<div class="widget-heading">
Klaus-Uwe Mitterer
{{ request.user.get_full_name }}
</div>
<div class="widget-subheading">
Chief Expephalon Officer
{{ request.user.profile.role }}
</div>
</div>
<div class="widget-content-right header-user-info ml-3">

View file

@ -0,0 +1,57 @@
{% extends "backend/base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="fa fa-database">
</i>
</div>
<div>Database Settings - Create Setting
<div class="page-title-subheading">Create a new key-value setting
</div>
</div>
</div>
<div class="page-title-actions">
<button type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-plus"></i> New Setting
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 col-lg-0">
<div class="mb-3 card">
<div class="card-header-tab card-header-tab-animation card-header">
<div class="card-header-title">
<i class="header-icon lnr-apartment icon-gradient bg-love-kiss"> </i>
Create Setting
</div>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="tabs-eg-77">
<form method="POST">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="button" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-check"></i> Save
</button>
<a href="{% url "dbsettings" %}" class="btn-shadow mr-3 btn btn-danger">
<i class="fa fa-times"></i> Cancel
</a>
{% endbuttons %}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,57 @@
{% extends "backend/base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="fa fa-database">
</i>
</div>
<div>Database Settings - Edit Setting
<div class="page-title-subheading">Edit key and value of a setting
</div>
</div>
</div>
<div class="page-title-actions">
<button type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-plus"></i> New Setting
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 col-lg-0">
<div class="mb-3 card">
<div class="card-header-tab card-header-tab-animation card-header">
<div class="card-header-title">
<i class="header-icon lnr-apartment icon-gradient bg-love-kiss"> </i>
Deleting {{ form.key.value }}
</div>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="tabs-eg-77">
<form method="POST">
{% csrf_token %}
Are you sure you wish to delete {{ form.key.value }}?
{% buttons %}
<button type="button" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-check"></i> Save
</button>
<a href="{% url "dbsettings" %}" class="btn-shadow mr-3 btn btn-danger">
<i class="fa fa-times"></i> Cancel
</a>
{% endbuttons %}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -13,9 +13,9 @@
</div>
</div>
<div class="page-title-actions">
<button type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success">
<a href="{% url "dbsettings_create" %}" type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-plus"></i> New Setting
</button>
</a>
</div>
</div>
@ -45,8 +45,8 @@
{% for setting in object_list %}
<tr>
<th scope="row">{{ setting.key }}</th>
<td><a href="javascript:void(0);">Click to display...</a></td>
<td><a href="/devices/20/edit"><i class="fas fa-edit" title="Edit Setting"></i></a> <a href="#"><i style="color: darkred;" onclick="askdelete(20);" class="fas fa-trash-alt" title="Delete Setting"></i></a></td>
<td><a href="javascript:alert('{{ setting.value }}');">Click to display...</a></td>
<td><a href="{% url "dbsettings_edit" setting.key %}"><i class="fas fa-edit" title="Edit Setting"></i></a> <a href="#"><i style="color: darkred;" onclick="askdelete(20);" class="fas fa-trash-alt" title="Delete Setting"></i></a></td>
</tr>
{% endfor %}
</tbody>

View file

@ -0,0 +1,57 @@
{% extends "backend/base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="fa fa-database">
</i>
</div>
<div>Database Settings - Edit Setting
<div class="page-title-subheading">Edit key and value of a setting
</div>
</div>
</div>
<div class="page-title-actions">
<button type="button" data-toggle="tooltip" title="New Setting" data-placement="bottom" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-plus"></i> New Setting
</button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 col-lg-0">
<div class="mb-3 card">
<div class="card-header-tab card-header-tab-animation card-header">
<div class="card-header-title">
<i class="header-icon lnr-apartment icon-gradient bg-love-kiss"> </i>
Editing {{ form.key.value }}
</div>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="tabs-eg-77">
<form method="POST">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="button" class="btn-shadow mr-3 btn btn-success">
<i class="fa fa-check"></i> Save
</button>
<a href="{% url "dbsettings" %}" class="btn-shadow mr-3 btn btn-danger">
<i class="fa fa-times"></i> Cancel
</a>
{% endbuttons %}
</form>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,26 +1,34 @@
<html>
<head>
<link rel="stylesheet" href="{% static "backend/css/login.css">
<link href="https://fontproxy.kumi.systems/css?family=Ubuntu" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://fa.kumi.systems/css/all.css">
<title>Sign in</title>
</head>
<body>
<div class="main">
<p class="sign" align="center">Sign in</p>
{% csrf_token %}
<form class="form1">
<input class="un " type="text" align="center" name="username" placeholder="Username">
<input class="pass" type="password" align="center" name="password" placeholder="Password">
<a class="submit" align="center">Sign in</a>
<p class="forgot" align="center"><a href="#">Forgot Password?</p>
{% extends "backend/auth_base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="mx-auto app-login-box col-sm-12 col-md-10 col-lg-9">
<div class="app-logo"></div>
<h4 class="mb-0">
<span class="d-block">Welcome back,</span>
<span>Please sign in to your account.</span></h4>
<h6 class="mt-3">No account? <a href="javascript:void(0);" class="text-primary">Sign up now</a></h6>
{% bootstrap_messages %}
<div class="divider row"></div>
<div>
<form method="POST" class="">
{% csrf_token %}
<div class="form-row">
<div class="col-md-6">
<div class="position-relative form-group"><label for="exampleEmail" class="">Email</label><input name="email" id="exampleEmail" placeholder="Email here..." type="email" class="form-control"></div>
</div>
<div class="col-md-6">
<div class="position-relative form-group"><label for="examplePassword" class="">Password</label><input name="password" id="examplePassword" placeholder="Password here..." type="password"
class="form-control"></div>
</div>
</div>
<div class="position-relative form-check"><input name="check" id="exampleCheck" type="checkbox" class="form-check-input"><label for="exampleCheck" class="form-check-label">Keep me logged in</label></div>
<div class="divider row"></div>
<div class="d-flex align-items-center">
<div class="ml-auto"><a href="javascript:void(0);" class="btn-lg btn btn-link">Recover Password</a>
<button class="btn btn-primary btn-lg">Login to Dashboard</button>
</div>
</div>
</form>
</div>
</body>
</html>
</div>
{% endblock %}

View file

@ -0,0 +1,40 @@
{% extends "backend/base.html" %}
{% block content %}
<div class="app-page-title">
<div class="page-title-wrapper">
<div class="page-title-heading">
<div class="page-title-icon">
<i class="fa fa-times">
</i>
</div>
<div>Oops!
<div class="page-title-subheading">This is not implemented yet...
</div>
</div>
</div>
<div class="page-title-actions">
</div>
</div>
</div>
<div class="row">
<div class="col-md-12 col-lg-0">
<div class="mb-3 card">
<div class="card-header-tab card-header-tab-animation card-header">
<div class="card-header-title">
<i class="header-icon lnr-apartment icon-gradient bg-love-kiss"> </i>
But here's something for you:
</div>
</div>
<div class="card-body">
<div class="tab-content">
<div class="tab-pane fade show active" id="tabs-eg-77">
<div class="card mb-3 widget-chart widget-chart2 text-left w-100">
<iframe width="1020" height="630" src="https://www.youtube-nocookie.com/embed/VFZNvj-HfBU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,27 @@
{% extends "backend/auth_base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="mx-auto app-login-box col-sm-12 col-md-10 col-lg-9">
<div class="app-logo"></div>
<h4 class="mb-0">
<span class="d-block">Welcome back,</span>
<span>Please select your Two-Factor Authentication provider</span></h4>
{% bootstrap_messages %}
<div class="divider row"></div>
<div>
<form method="POST" class="">
{% csrf_token %}
<div class="form-row">
{% bootstrap_form form %}
</div>
<div class="position-relative form-check"><input name="check" id="exampleCheck" type="checkbox" class="form-check-input"><label for="exampleCheck" class="form-check-label">Keep me logged in</label></div>
<div class="divider row"></div>
<div class="d-flex align-items-center">
<div class="ml-auto"><a href="javascript:void(0);" class="btn-lg btn btn-link">Recover Password</a>
<button class="btn btn-primary btn-lg">Login to Dashboard</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,27 @@
{% extends "backend/auth_base.html" %}
{% load bootstrap4 %}
{% block content %}
<div class="mx-auto app-login-box col-sm-12 col-md-10 col-lg-9">
<div class="app-logo"></div>
<h4 class="mb-0">
<span class="d-block">Welcome back,</span>
<span>Please enter your OTP Token</span></h4>
{% bootstrap_messages %}
<div class="divider row"></div>
<div>
<form method="POST" class="">
{% csrf_token %}
<div class="form-row">
{% bootstrap_form form %}
</div>
<div class="position-relative form-check"><input name="check" id="exampleCheck" type="checkbox" class="form-check-input"><label for="exampleCheck" class="form-check-label">Keep me logged in</label></div>
<div class="divider row"></div>
<div class="d-flex align-items-center">
<div class="ml-auto"><a href="javascript:void(0);" class="btn-lg btn btn-link">Recover Password</a>
<button class="btn btn-primary btn-lg">Login to Dashboard</button>
</div>
</div>
</form>
</div>
</div>
{% endblock %}