mirror of
				https://github.com/retspen/webvirtcloud
				synced 2025-07-31 12:41:08 +00:00 
			
		
		
		
	
						commit
						116b39018a
					
				
					 7 changed files with 129 additions and 19 deletions
				
			
		|  | @ -1,6 +1,9 @@ | |||
| {% extends "base.html" %} | ||||
| 
 | ||||
| {% load i18n %} | ||||
| {% load icons %} | ||||
| {% load qr_code %} | ||||
| 
 | ||||
| {% block title %}{% trans "User Profile" %} - {{ user }}{% endblock %} | ||||
| {% block content %} | ||||
|     <!-- Page Heading --> | ||||
|  | @ -23,6 +26,11 @@ | |||
|         <li class="nav-item"> | ||||
|             <a class="nav-link" data-toggle="tab" href="#public-keys">{% trans "Public Keys" %}</a> | ||||
|         </li> | ||||
|         {% if totp_url %} | ||||
|         <li class="nav-item"> | ||||
|             <a class="nav-link" data-toggle="tab" href="#otp">{% trans "OTP QR Code" %}</a> | ||||
|         </li> | ||||
|         {% endif %} | ||||
|     </ul> | ||||
| 
 | ||||
|     <div class="tab-content"> | ||||
|  | @ -79,5 +87,12 @@ | |||
|                 </tbody> | ||||
|             </table> | ||||
|         </div> | ||||
|         {% if totp_url %} | ||||
|         <div class="tab-pane fade" id="otp"> | ||||
|             <div class="text-center"> | ||||
|                 {% qr_from_text totp_url image_format="png" %} | ||||
|             </div> | ||||
|         </div> | ||||
|         {% endif %} | ||||
|     </div> | ||||
| {% endblock content %} | ||||
|  |  | |||
							
								
								
									
										62
									
								
								accounts/templates/accounts/otp_login.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								accounts/templates/accounts/otp_login.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | |||
| {% load i18n %} | ||||
| {% load static %} | ||||
| {% load bootstrap4 %} | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| 
 | ||||
| <head> | ||||
| 
 | ||||
|     <meta charset="utf-8"> | ||||
|     <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <meta name="description" content="WebVirtMgr panel for manage virtual machine"> | ||||
|     <meta name="author" content="anatoliy.guskov@gmail.com"> | ||||
| 
 | ||||
|     <title>{% trans "WebVirtCloud" %}  - {% trans "Sign In" %}</title> | ||||
| 
 | ||||
|     <!-- Bootstrap Core CSS --> | ||||
|     <link href="{% static "css/wvc-main.min.css" %}" rel="stylesheet" id="wvc_css"> | ||||
| 
 | ||||
|     <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> | ||||
|     <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> | ||||
|     <!--[if lt IE 9]> | ||||
|         <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> | ||||
|         <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> | ||||
|     <![endif]--> | ||||
| 
 | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
|     <div class="container"> | ||||
|         <div> | ||||
|             <div class="page-header text-center"> | ||||
|                 <a class="" href="/"><h1>WebVirtCloud</h1></a> | ||||
|             </div> | ||||
|             <div class="row"> | ||||
|                 <div class="col-6 offset-3" role="main"> | ||||
|                     <div class="card"> | ||||
|                         <div class="card-body"> | ||||
|                             {% if form.errors %} | ||||
|                                 {% bootstrap_form_errors form %} | ||||
|                             {% endif %} | ||||
|                             <form class="form-signin" method="post" role="form" aria-label="Login form"> | ||||
|                                 {% csrf_token %} | ||||
|                                 {% bootstrap_field form.username layout='horizontal' %} | ||||
|                                 {% bootstrap_field form.password layout='horizontal' %} | ||||
|                                 {% bootstrap_field form.otp_token layout='horizontal' %} | ||||
|                                 <button class="btn btn-lg btn-success btn-block" type="submit">{% trans "Sign In" %}</button> | ||||
|                             </form> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- jQuery --> | ||||
|     <script src="{% static "js/jquery.js" %}"></script> | ||||
|     <!-- Bootstrap Core JavaScript --> | ||||
|     <script src="{% static "js/bootstrap.bundle.min.js" %}"></script> | ||||
| </body> | ||||
| 
 | ||||
| </html> | ||||
|  | @ -1,11 +1,12 @@ | |||
| from django.contrib.auth import views as auth_views | ||||
| from django.conf import settings | ||||
| from django.contrib.auth.views import LoginView, LogoutView | ||||
| from django.urls import path | ||||
| from django_otp.forms import OTPAuthenticationForm | ||||
| 
 | ||||
| from . import views | ||||
| 
 | ||||
| urlpatterns = [ | ||||
|     path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'), | ||||
|     path('logout/', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'), | ||||
|     path('logout/', LogoutView.as_view(template_name='logout.html'), name='logout'), | ||||
|     path('profile/', views.profile, name='profile'), | ||||
|     path('profile/<int:user_id>/', views.account, name='account'), | ||||
|     path('change_password/', views.change_password, name='change_password'), | ||||
|  | @ -13,3 +14,12 @@ urlpatterns = [ | |||
|     path('user_instance/<int:pk>/update/', views.user_instance_update, name='user_instance_update'), | ||||
|     path('user_instance/<int:pk>/delete/', views.user_instance_delete, name='user_instance_delete'), | ||||
| ] | ||||
| 
 | ||||
| if settings.OTP_ENABLED: | ||||
|     urlpatterns += path( | ||||
|         'login/', | ||||
|         LoginView.as_view(template_name='accounts/otp_login.html', authentication_form=OTPAuthenticationForm), | ||||
|         name='login', | ||||
|     ), | ||||
| else: | ||||
|     urlpatterns += path('login/', LoginView.as_view(template_name='login.html'), name='login'), | ||||
|  |  | |||
							
								
								
									
										9
									
								
								accounts/utils.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								accounts/utils.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| from django_otp import devices_for_user | ||||
| from django_otp.plugins.otp_totp.models import TOTPDevice | ||||
| 
 | ||||
| 
 | ||||
| def get_user_totp_device(user): | ||||
|     devices = devices_for_user(user) | ||||
|     for device in devices: | ||||
|         if isinstance(device, TOTPDevice): | ||||
|             return device | ||||
|  | @ -1,20 +1,18 @@ | |||
| import os | ||||
| 
 | ||||
| from admin.decorators import superuser_only | ||||
| from django.contrib import messages | ||||
| from django.contrib.auth import update_session_auth_hash | ||||
| from django.contrib.auth.decorators import permission_required | ||||
| from django.contrib.auth.forms import PasswordChangeForm | ||||
| from django.core.validators import ValidationError | ||||
| from django.http import HttpResponseRedirect | ||||
| from django.shortcuts import get_object_or_404, redirect, render | ||||
| from django.urls import reverse | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
| 
 | ||||
| from accounts.models import * | ||||
| from admin.decorators import superuser_only | ||||
| from instances.models import Instance | ||||
| 
 | ||||
| from accounts.models import * | ||||
| 
 | ||||
| from . import forms | ||||
| from .utils import get_user_totp_device | ||||
| 
 | ||||
| 
 | ||||
| def profile(request): | ||||
|  | @ -44,7 +42,10 @@ def profile(request): | |||
|                 error_messages.append(msg) | ||||
|             if not error_messages: | ||||
|                 addkeypublic = UserSSHKey( | ||||
|                     user_id=request.user.id, keyname=keyname, keypublic=keypublic) | ||||
|                     user_id=request.user.id, | ||||
|                     keyname=keyname, | ||||
|                     keypublic=keypublic, | ||||
|                 ) | ||||
|                 addkeypublic.save() | ||||
|                 return HttpResponseRedirect(request.get_full_path()) | ||||
|         if "keydelete" in request.POST: | ||||
|  | @ -62,6 +63,11 @@ def account(request, user_id): | |||
|     user_insts = UserInstance.objects.filter(user_id=user_id) | ||||
|     instances = Instance.objects.all().order_by("name") | ||||
|     publickeys = UserSSHKey.objects.filter(user_id=user_id) | ||||
|     if settings.OTP_ENABLED: | ||||
|         device = get_user_totp_device(user) | ||||
|         if not device: | ||||
|             device = user.totpdevice_set.create() | ||||
|         totp_url = device.config_url | ||||
| 
 | ||||
|     return render(request, "account.html", locals()) | ||||
| 
 | ||||
|  | @ -79,11 +85,7 @@ def change_password(request): | |||
|             messages.error(request, _("Wrong Data Provided")) | ||||
|     else: | ||||
|         form = PasswordChangeForm(request.user) | ||||
|     return render( | ||||
|         request, | ||||
|         "accounts/change_password_form.html", | ||||
|         {"form": form} | ||||
|     ) | ||||
|     return render(request, "accounts/change_password_form.html", {"form": form}) | ||||
| 
 | ||||
| 
 | ||||
| @superuser_only | ||||
|  |  | |||
|  | @ -1,16 +1,21 @@ | |||
| beautifulsoup4==4.9.1 | ||||
| beautifulsoup4==4.9.3 | ||||
| Django==2.2.16 | ||||
| django-bootstrap4==2.2.0 | ||||
| django-icons==2.1.1 | ||||
| django-login-required-middleware==0.5.0 | ||||
| django-otp==1.0.1 | ||||
| django-qr-code==1.3.1 | ||||
| gunicorn==20.0.4 | ||||
| importlib-metadata==1.7.0 | ||||
| libsass==0.20.1 | ||||
| libvirt-python==6.7.0 | ||||
| libvirt-python==6.8.0 | ||||
| lxml==4.5.2 | ||||
| numpy==1.18.5 | ||||
| numpy==1.19.2 | ||||
| pytz==2020.1 | ||||
| qrcode==6.1 | ||||
| rwlock==0.0.7 | ||||
| six==1.15.0 | ||||
| soupsieve==2.0.1 | ||||
| sqlparse==0.3.1 | ||||
| sqlparse==0.4.1 | ||||
| websockify==0.9.0 | ||||
| zipp==3.3.0 | ||||
|  |  | |||
|  | @ -23,6 +23,8 @@ INSTALLED_APPS = [ | |||
|     'django.contrib.staticfiles', | ||||
|     'bootstrap4', | ||||
|     'django_icons', | ||||
|     'django_otp', | ||||
|     'django_otp.plugins.otp_totp', | ||||
|     'accounts', | ||||
|     'admin', | ||||
|     'appsettings', | ||||
|  | @ -36,6 +38,7 @@ INSTALLED_APPS = [ | |||
|     'storages', | ||||
|     'secrets', | ||||
|     'logs', | ||||
|     'qr_code', | ||||
| ] | ||||
| 
 | ||||
| MIDDLEWARE = [ | ||||
|  | @ -45,6 +48,7 @@ MIDDLEWARE = [ | |||
|     'django.middleware.common.CommonMiddleware', | ||||
|     'django.middleware.csrf.CsrfViewMiddleware', | ||||
|     'django.contrib.auth.middleware.AuthenticationMiddleware', | ||||
|     'django_otp.middleware.OTPMiddleware', | ||||
|     'login_required.middleware.LoginRequiredMiddleware', | ||||
|     'django.contrib.auth.middleware.RemoteUserMiddleware', | ||||
|     'django.contrib.messages.middleware.MessageMiddleware', | ||||
|  | @ -176,3 +180,6 @@ LIBVIRT_KEEPALIVE_COUNT = 5 | |||
| ALLOW_EMPTY_PASSWORD = False | ||||
| NEW_USER_DEFAULT_INSTANCES = [] | ||||
| SHOW_PROFILE_EDIT_PASSWORD = True | ||||
| 
 | ||||
| OTP_ENABLED = False | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue