From b8ca7286f2e2767a56e0b21f24bd3f25d2abdbbf Mon Sep 17 00:00:00 2001 From: William Colmenares Date: Thu, 9 May 2019 01:34:18 -0400 Subject: [PATCH 1/7] Add view to check if the vm belongs to a user --- hosting/urls.py | 3 ++- hosting/views.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/hosting/urls.py b/hosting/urls.py index a3579f06..1d0033ef 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -9,13 +9,14 @@ from .views import ( HostingPricingView, CreateVirtualMachinesView, HostingBillListView, HostingBillDetailView, SSHKeyDeleteView, SSHKeyCreateView, SSHKeyListView, SSHKeyChoiceView, DashboardView, SettingsView, ResendActivationEmailView, - InvoiceListView, InvoiceDetailView + InvoiceListView, InvoiceDetailView, CheckUserVM ) urlpatterns = [ url(r'index/?$', IndexView.as_view(), name='index'), url(r'django/?$', DjangoHostingView.as_view(), name='djangohosting'), + url(r'checkvm/?$', CheckUserVM.as_view(), name='chech_vm'), url(r'dashboard/?$', DashboardView.as_view(), name='dashboard'), url(r'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'), url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'), diff --git a/hosting/views.py b/hosting/views.py index 92dd5aa8..4c0d8b41 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -26,6 +26,8 @@ from django.views.generic import ( View, CreateView, FormView, ListView, DetailView, DeleteView, TemplateView, UpdateView ) +from rest_framework.views import APIView +from rest_framework.response import Response from guardian.mixins import PermissionRequiredMixin from oca.pool import WrongIdError from stored_messages.api import mark_read @@ -1755,3 +1757,21 @@ def forbidden_view(request, exception=None, reason=''): 'again.') messages.add_message(request, messages.ERROR, err_msg) return HttpResponseRedirect(request.get_full_path()) + + +class CheckUserVM(APIView): + + def get(self, request): + try: + email = request.data['email'] + ip = request.data['ip'] + uservms = VMDetail.objects.filter(user__email=email) + if len(uservms) > 0: + for i in range(len(uservms)): + if uservms[i].ipv4 == ip or uservms[i].ipv6 == ip: + return Response('success', 200) + return Response('No VM found matching the ip address provided', 403) + else: + return Response('No VM found with the given email address', 403) + except KeyError: + return Response('Not enough data provided', 400) From 1faf46cc1b57a1e737d8ba694d3dff1e2ada3a36 Mon Sep 17 00:00:00 2001 From: William Colmenares Date: Sun, 12 May 2019 21:34:10 -0400 Subject: [PATCH 2/7] added validation to heck if the user is the one allowed to access --- hosting/views.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index 4c0d8b41..f39e1b58 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1,5 +1,7 @@ import logging import uuid +import os +import dotenv from datetime import datetime from time import sleep @@ -28,6 +30,7 @@ from django.views.generic import ( ) from rest_framework.views import APIView from rest_framework.response import Response +from rest_framework.renderers import JSONRenderer from guardian.mixins import PermissionRequiredMixin from oca.pool import WrongIdError from stored_messages.api import mark_read @@ -36,7 +39,7 @@ from stored_messages.settings import stored_messages_settings from datacenterlight.cms_models import DCLCalculatorPluginModel from datacenterlight.models import VMTemplate, VMPricing -from datacenterlight.utils import create_vm, get_cms_integration +from datacenterlight.utils import create_vm, get_cms_integration, check_otp, env from hosting.models import UserCardDetail from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager @@ -68,9 +71,12 @@ from .models import ( logger = logging.getLogger(__name__) + CONNECTION_ERROR = "Your VMs cannot be displayed at the moment due to a \ backend connection error. please try again in a few \ minutes." + + decorators = [never_cache] @@ -1760,11 +1766,20 @@ def forbidden_view(request, exception=None, reason=''): class CheckUserVM(APIView): + renderer_classes = (JSONRenderer, ) def get(self, request): try: email = request.data['email'] ip = request.data['ip'] + user = request.data['user'] + realm = request.data['realm'] + token = request.data['token'] + if user != env('ACCOUNT_NAME'): + return Response("User not allowed", 403) + response = check_otp(user, realm, token) + if response != 200: + return Response('Invalid token', 403) uservms = VMDetail.objects.filter(user__email=email) if len(uservms) > 0: for i in range(len(uservms)): From fda5118c39029d27c369e4fc762b6736e3d7e1fb Mon Sep 17 00:00:00 2001 From: William Colmenares Date: Sun, 12 May 2019 21:34:54 -0400 Subject: [PATCH 3/7] Added otp verification --- datacenterlight/utils.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index bbcb16ab..808a7643 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,4 +1,8 @@ import logging +import os +import pyotp +import requests +import dotenv from django.contrib.sites.models import Site from datacenterlight.tasks import create_vm_task @@ -11,6 +15,17 @@ from .models import VMPricing, VMTemplate logger = logging.getLogger(__name__) +PROJECT_DIR = os.path.abspath( + os.path.join(os.path.dirname(__file__)), +) + +# load .env file +dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR)) + + +def env(env_name): + return os.environ.get(env_name) + def get_cms_integration(name): current_site = Site.objects.get_current() @@ -100,3 +115,22 @@ def clear_all_session_vars(request): 'generic_payment_details', 'product_id']: if session_var in request.session: del request.session[session_var] + + +def check_otp(name, realm, token): + data = { + "auth_name": env('AUTH_NAME'), + "auth_token": pyotp.TOTP(env('AUTH_SEED')).now(), + "auth_realm": env('AUTH_REALM'), + "name": name, + "realm": realm, + "token": token + } + response = requests.post( + "https://{OTP_SERVER}{OTP_VERIFY_ENDPOINT}".format( + OTP_SERVER=env('OTP_SERVER'), + OTP_VERIFY_ENDPOINT=env('OTP_VERIFY_ENDPOINT') + ), + data=data + ) + return response.status_code From 5e2e906f48d70c12244fda0557e4c1c1ce923df0 Mon Sep 17 00:00:00 2001 From: William Colmenares Date: Sun, 12 May 2019 21:35:28 -0400 Subject: [PATCH 4/7] include pyotp in requeriments --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index fe70299b..22da4c26 100644 --- a/requirements.txt +++ b/requirements.txt @@ -98,3 +98,4 @@ amqp==2.2.1 vine==1.1.4 cdist==4.7.0 git+https://github.com/ungleich/djangocms-multisite.git#egg=djangocms_multisite +pyotp From 69ec7d2b4627a37e555a29e087d6803c49f37050 Mon Sep 17 00:00:00 2001 From: William Colmenares Date: Mon, 13 May 2019 03:44:09 -0400 Subject: [PATCH 5/7] reuse of the env variable in the base settings --- datacenterlight/utils.py | 25 ++++++------------------- dynamicweb/settings/base.py | 8 ++++++++ hosting/views.py | 7 +++---- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/datacenterlight/utils.py b/datacenterlight/utils.py index 808a7643..208d39f3 100644 --- a/datacenterlight/utils.py +++ b/datacenterlight/utils.py @@ -1,9 +1,8 @@ import logging -import os import pyotp import requests -import dotenv from django.contrib.sites.models import Site +from django.conf import settings from datacenterlight.tasks import create_vm_task from hosting.models import HostingOrder, HostingBill, OrderDetail @@ -15,18 +14,6 @@ from .models import VMPricing, VMTemplate logger = logging.getLogger(__name__) -PROJECT_DIR = os.path.abspath( - os.path.join(os.path.dirname(__file__)), -) - -# load .env file -dotenv.read_dotenv("{0}/.env".format(PROJECT_DIR)) - - -def env(env_name): - return os.environ.get(env_name) - - def get_cms_integration(name): current_site = Site.objects.get_current() try: @@ -119,17 +106,17 @@ def clear_all_session_vars(request): def check_otp(name, realm, token): data = { - "auth_name": env('AUTH_NAME'), - "auth_token": pyotp.TOTP(env('AUTH_SEED')).now(), - "auth_realm": env('AUTH_REALM'), + "auth_name": settings.AUTH_NAME, + "auth_token": pyotp.TOTP(settings.AUTH_SEED).now(), + "auth_realm": settings.AUTH_REALM, "name": name, "realm": realm, "token": token } response = requests.post( "https://{OTP_SERVER}{OTP_VERIFY_ENDPOINT}".format( - OTP_SERVER=env('OTP_SERVER'), - OTP_VERIFY_ENDPOINT=env('OTP_VERIFY_ENDPOINT') + OTP_SERVER=settings.OTP_SERVER, + OTP_VERIFY_ENDPOINT=settings.OTP_VERIFY_ENDPOINT ), data=data ) diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index b267c31d..27909813 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -721,6 +721,14 @@ X_FRAME_OPTIONS = ('SAMEORIGIN' if X_FRAME_OPTIONS_ALLOW_FROM_URI is None else DEBUG = bool_env('DEBUG') +ACCOUNT_NAME = env('ACCOUNT_NAME') +AUTH_NAME = env('AUTH_NAME') +AUTH_SEED = env('AUTH_SEED') +AUTH_REALM = env('AUTH_REALM') +OTP_SERVER = env('OTP_SERVER') +OTP_VERIFY_ENDPOINT = env('OTP_VERIFY_ENDPOINT') + + if DEBUG: from .local import * # flake8: noqa else: diff --git a/hosting/views.py b/hosting/views.py index f39e1b58..b0cee45c 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1,7 +1,5 @@ import logging import uuid -import os -import dotenv from datetime import datetime from time import sleep @@ -39,7 +37,7 @@ from stored_messages.settings import stored_messages_settings from datacenterlight.cms_models import DCLCalculatorPluginModel from datacenterlight.models import VMTemplate, VMPricing -from datacenterlight.utils import create_vm, get_cms_integration, check_otp, env +from datacenterlight.utils import create_vm, get_cms_integration, check_otp from hosting.models import UserCardDetail from membership.models import CustomUser, StripeCustomer from opennebula_api.models import OpenNebulaManager @@ -1775,7 +1773,8 @@ class CheckUserVM(APIView): user = request.data['user'] realm = request.data['realm'] token = request.data['token'] - if user != env('ACCOUNT_NAME'): + print(settings.ACCOUNT_NAME) + if user != settings.ACCOUNT_NAME: return Response("User not allowed", 403) response = check_otp(user, realm, token) if response != 200: From ce630573e027fdea9a6cad8c31bf6620e2b5bc89 Mon Sep 17 00:00:00 2001 From: William Colmenares Date: Thu, 16 May 2019 13:33:31 -0400 Subject: [PATCH 6/7] Remove print statement & correct code return --- hosting/views.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hosting/views.py b/hosting/views.py index b0cee45c..88adaf22 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -1773,7 +1773,6 @@ class CheckUserVM(APIView): user = request.data['user'] realm = request.data['realm'] token = request.data['token'] - print(settings.ACCOUNT_NAME) if user != settings.ACCOUNT_NAME: return Response("User not allowed", 403) response = check_otp(user, realm, token) @@ -1784,8 +1783,8 @@ class CheckUserVM(APIView): for i in range(len(uservms)): if uservms[i].ipv4 == ip or uservms[i].ipv6 == ip: return Response('success', 200) - return Response('No VM found matching the ip address provided', 403) + return Response('No VM found matching the ip address provided', 404) else: - return Response('No VM found with the given email address', 403) + return Response('No VM found with the given email address', 404) except KeyError: return Response('Not enough data provided', 400) From a82ecbe4d50d7bd3e7fd0497b930ad4844f58309 Mon Sep 17 00:00:00 2001 From: William Colmenares Date: Thu, 16 May 2019 13:34:13 -0400 Subject: [PATCH 7/7] fix typho in check_vm --- hosting/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/urls.py b/hosting/urls.py index 1d0033ef..2c8ff8ab 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -16,7 +16,7 @@ from .views import ( urlpatterns = [ url(r'index/?$', IndexView.as_view(), name='index'), url(r'django/?$', DjangoHostingView.as_view(), name='djangohosting'), - url(r'checkvm/?$', CheckUserVM.as_view(), name='chech_vm'), + url(r'checkvm/?$', CheckUserVM.as_view(), name='check_vm'), url(r'dashboard/?$', DashboardView.as_view(), name='dashboard'), url(r'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'), url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'),