diff options
Diffstat (limited to 'dashboard/src/account')
-rw-r--r-- | dashboard/src/account/__init__.py | 2 | ||||
-rw-r--r-- | dashboard/src/account/admin.py | 5 | ||||
-rw-r--r-- | dashboard/src/account/jira_util.py | 2 | ||||
-rw-r--r-- | dashboard/src/account/migrations/0001_initial.py | 33 | ||||
-rw-r--r-- | dashboard/src/account/migrations/0002_auto_20180110_1636.py | 33 | ||||
-rw-r--r-- | dashboard/src/account/migrations/0002_lab_description.py | 19 | ||||
-rw-r--r-- | dashboard/src/account/migrations/0003_auto_20180110_1639.py | 24 | ||||
-rw-r--r-- | dashboard/src/account/migrations/0003_publicnetwork.py | 25 | ||||
-rw-r--r-- | dashboard/src/account/migrations/__init__.py | 10 | ||||
-rw-r--r-- | dashboard/src/account/models.py | 125 | ||||
-rw-r--r-- | dashboard/src/account/tasks.py | 4 | ||||
-rw-r--r-- | dashboard/src/account/tests/__init__.py | 2 | ||||
-rw-r--r-- | dashboard/src/account/tests/test_general.py | 6 | ||||
-rw-r--r-- | dashboard/src/account/urls.py | 29 | ||||
-rw-r--r-- | dashboard/src/account/views.py | 145 |
15 files changed, 377 insertions, 87 deletions
diff --git a/dashboard/src/account/__init__.py b/dashboard/src/account/__init__.py index b5914ce..b6fef6c 100644 --- a/dashboard/src/account/__init__.py +++ b/dashboard/src/account/__init__.py @@ -6,5 +6,3 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## - - diff --git a/dashboard/src/account/admin.py b/dashboard/src/account/admin.py index 6f77122..b4c142c 100644 --- a/dashboard/src/account/admin.py +++ b/dashboard/src/account/admin.py @@ -1,5 +1,6 @@ ############################################################################## # Copyright (c) 2016 Max Breitenfeldt and others. +# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 @@ -10,7 +11,9 @@ from django.contrib import admin -from account.models import UserProfile, Lab +from account.models import UserProfile, Lab, VlanManager, PublicNetwork admin.site.register(UserProfile) admin.site.register(Lab) +admin.site.register(VlanManager) +admin.site.register(PublicNetwork) diff --git a/dashboard/src/account/jira_util.py b/dashboard/src/account/jira_util.py index fdb87f7..18b0e26 100644 --- a/dashboard/src/account/jira_util.py +++ b/dashboard/src/account/jira_util.py @@ -62,4 +62,4 @@ def get_jira(user): 'key_cert': key_cert } - return JIRA(server=settings.JIRA_URL, oauth=oauth_dict)
\ No newline at end of file + return JIRA(server=settings.JIRA_URL, oauth=oauth_dict) diff --git a/dashboard/src/account/migrations/0001_initial.py b/dashboard/src/account/migrations/0001_initial.py index 591f702..c8b5bdc 100644 --- a/dashboard/src/account/migrations/0001_initial.py +++ b/dashboard/src/account/migrations/0001_initial.py @@ -1,6 +1,4 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10 on 2016-11-03 13:33 -from __future__ import unicode_literals +# Generated by Django 2.1 on 2018-09-14 14:48 import account.models from django.conf import settings @@ -18,21 +16,50 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( + name='Lab', + fields=[ + ('name', models.CharField(max_length=200, primary_key=True, serialize=False, unique=True)), + ('contact_email', models.EmailField(blank=True, max_length=200, null=True)), + ('contact_phone', models.CharField(blank=True, max_length=20, null=True)), + ('status', models.IntegerField(default=0)), + ('location', models.TextField(default='unknown')), + ('api_token', models.CharField(max_length=50)), + ('lab_user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( name='UserProfile', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('timezone', models.CharField(default='UTC', max_length=100)), ('ssh_public_key', models.FileField(blank=True, null=True, upload_to=account.models.upload_to)), ('pgp_public_key', models.FileField(blank=True, null=True, upload_to=account.models.upload_to)), + ('email_addr', models.CharField(default='email@mail.com', max_length=300)), ('company', models.CharField(max_length=200)), ('oauth_token', models.CharField(max_length=1024)), ('oauth_secret', models.CharField(max_length=1024)), ('jira_url', models.CharField(default='', max_length=100)), ('full_name', models.CharField(default='', max_length=100)), + ('booking_privledge', models.BooleanField(default=False)), ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], options={ 'db_table': 'user_profile', }, ), + migrations.CreateModel( + name='VlanManager', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('vlans', models.TextField()), + ('block_size', models.IntegerField()), + ('allow_overlapping', models.BooleanField()), + ('reserved_vlans', models.TextField()), + ], + ), + migrations.AddField( + model_name='lab', + name='vlan_manager', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='account.VlanManager'), + ), ] diff --git a/dashboard/src/account/migrations/0002_auto_20180110_1636.py b/dashboard/src/account/migrations/0002_auto_20180110_1636.py deleted file mode 100644 index 6170acb..0000000 --- a/dashboard/src/account/migrations/0002_auto_20180110_1636.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10 on 2018-01-10 16:36 -from __future__ import unicode_literals - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('account', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Lab', - fields=[ - ('id', models.CharField(max_length=200, primary_key=True, serialize=False)), - ('name', models.CharField(max_length=200, unique=True)), - ('contact_email', models.EmailField(blank=True, max_length=200, null=True)), - ('contact_phone', models.CharField(blank=True, max_length=20, null=True)), - ('lab_user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), - ], - ), - migrations.AddField( - model_name='userprofile', - name='email_addr', - field=models.CharField(default='email@mail.com', max_length=300), - ), - ] diff --git a/dashboard/src/account/migrations/0002_lab_description.py b/dashboard/src/account/migrations/0002_lab_description.py new file mode 100644 index 0000000..445501a --- /dev/null +++ b/dashboard/src/account/migrations/0002_lab_description.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1 on 2018-09-14 20:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='lab', + name='description', + field=models.CharField(default='Lab description default', max_length=240), + preserve_default=False, + ), + ] diff --git a/dashboard/src/account/migrations/0003_auto_20180110_1639.py b/dashboard/src/account/migrations/0003_auto_20180110_1639.py deleted file mode 100644 index d0bc4d6..0000000 --- a/dashboard/src/account/migrations/0003_auto_20180110_1639.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10 on 2018-01-10 16:39 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('account', '0002_auto_20180110_1636'), - ] - - operations = [ - migrations.RemoveField( - model_name='lab', - name='id', - ), - migrations.AlterField( - model_name='lab', - name='name', - field=models.CharField(max_length=200, primary_key=True, serialize=False, unique=True), - ), - ] diff --git a/dashboard/src/account/migrations/0003_publicnetwork.py b/dashboard/src/account/migrations/0003_publicnetwork.py new file mode 100644 index 0000000..71e5caa --- /dev/null +++ b/dashboard/src/account/migrations/0003_publicnetwork.py @@ -0,0 +1,25 @@ +# Generated by Django 2.1 on 2018-09-26 14:41 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0002_lab_description'), + ] + + operations = [ + migrations.CreateModel( + name='PublicNetwork', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('vlan', models.IntegerField()), + ('in_use', models.BooleanField(default=False)), + ('cidr', models.CharField(default='0.0.0.0/0', max_length=50)), + ('gateway', models.CharField(default='0.0.0.0', max_length=50)), + ('lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')), + ], + ), + ] diff --git a/dashboard/src/account/migrations/__init__.py b/dashboard/src/account/migrations/__init__.py index b5914ce..e69de29 100644 --- a/dashboard/src/account/migrations/__init__.py +++ b/dashboard/src/account/migrations/__init__.py @@ -1,10 +0,0 @@ -############################################################################## -# Copyright (c) 2016 Max Breitenfeldt and others. -# -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Apache License, Version 2.0 -# which accompanies this distribution, and is available at -# http://www.apache.org/licenses/LICENSE-2.0 -############################################################################## - - diff --git a/dashboard/src/account/models.py b/dashboard/src/account/models.py index aad4c50..4fc7c40 100644 --- a/dashboard/src/account/models.py +++ b/dashboard/src/account/models.py @@ -10,11 +10,20 @@ from django.contrib.auth.models import User from django.db import models +import json +import random + + +class LabStatus(object): + UP = 0 + TEMP_DOWN = 100 + DOWN = 200 def upload_to(object, filename): return object.user.username + '/' + filename + class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) timezone = models.CharField(max_length=100, blank=False, default='UTC') @@ -28,6 +37,7 @@ class UserProfile(models.Model): jira_url = models.CharField(max_length=100, default='') full_name = models.CharField(max_length=100, default='') + booking_privledge = models.BooleanField(default=False) class Meta: db_table = 'user_profile' @@ -35,11 +45,126 @@ class UserProfile(models.Model): def __str__(self): return self.user.username + +class VlanManager(models.Model): + # list of length 4096 containing either 0 (not available) or 1 (available) + vlans = models.TextField() + block_size = models.IntegerField() + allow_overlapping = models.BooleanField() + # list of length 4096 containing either 0 (not rexerved) or 1 (reserved) + reserved_vlans = models.TextField() + + def get_vlan(self, count=1): + allocated = [] + vlans = json.loads(self.vlans) + for i in range(count): + new_vlan = vlans.index(1) # will throw if none available + vlans[new_vlan] = 0 + allocated.append(new_vlan) + if count == 1: + return allocated[0] + return allocated + + def get_public_vlan(self): + return PublicNetwork.objects.filter(lab=self.lab_set.first(), in_use=False).first() + + def reserve_public_vlan(self, vlan): + net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan, in_use=False) + net.in_use = True + net.save() + + def release_public_vlan(self, vlan): + net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan, in_use=True) + net.in_use = False + net.save() + + def public_vlan_is_available(self, vlan): + net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan) + return not net.in_use + + def is_available(self, vlans): + """ + 'vlans' is either a single vlan id integer or a list of integers + will return true (available) or false + """ + if self.allow_overlapping: + return True + + reserved = json.loads(self.reserved_vlans) + vlan_master_list = json.loads(self.vlans) + try: + iter(vlans) + except Exception: + vlans = [vlans] + + for vlan in vlans: + if not vlan_master_list[vlan] or reserved[vlan]: + return False + return True + + def release_vlans(self, vlans): + """ + 'vlans' is either a single vlan id integer or a list of integers + will make the vlans available + doesnt return a value + """ + my_vlans = json.loads(self.vlans) + + try: + iter(vlans) + except Exception: + vlans = [vlans] + + for vlan in vlans: + my_vlans[vlan] = 1 + self.vlans = json.dumps(my_vlans) + self.save() + + def reserve_vlans(self, vlans): + my_vlans = json.loads(self.vlans) + + try: + iter(vlans) + except Exception: + vlans = [vlans] + + vlans = set(vlans) + + for vlan in vlans: + if my_vlans[vlan] == 0: + raise ValueError("vlan " + str(vlan) + " is not available") + + my_vlans[vlan] = 0 + self.vlans = json.dumps(my_vlans) + self.save() + + class Lab(models.Model): lab_user = models.OneToOneField(User, on_delete=models.CASCADE) name = models.CharField(max_length=200, primary_key=True, unique=True, null=False, blank=False) contact_email = models.EmailField(max_length=200, null=True, blank=True) contact_phone = models.CharField(max_length=20, null=True, blank=True) + status = models.IntegerField(default=LabStatus.UP) + vlan_manager = models.ForeignKey(VlanManager, on_delete=models.CASCADE, null=True) + location = models.TextField(default="unknown") + api_token = models.CharField(max_length=50) + description = models.CharField(max_length=240) + + @staticmethod + def make_api_token(): + alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + key = "" + for i in range(45): + key += random.choice(alphabet) + return key def __str__(self): return self.name + + +class PublicNetwork(models.Model): + vlan = models.IntegerField() + lab = models.ForeignKey(Lab, on_delete=models.CASCADE) + in_use = models.BooleanField(default=False) + cidr = models.CharField(max_length=50, default="0.0.0.0/0") + gateway = models.CharField(max_length=50, default="0.0.0.0") diff --git a/dashboard/src/account/tasks.py b/dashboard/src/account/tasks.py index bfb865d..fe51974 100644 --- a/dashboard/src/account/tasks.py +++ b/dashboard/src/account/tasks.py @@ -1,5 +1,6 @@ ############################################################################## # Copyright (c) 2016 Max Breitenfeldt and others. +# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 @@ -28,7 +29,6 @@ def sync_jira_accounts(): user.email = user_dict['emailAddress'] user.userprofile.url = user_dict['self'] user.userprofile.full_name = user_dict['displayName'] - print(user_dict) user.userprofile.save() - user.save()
\ No newline at end of file + user.save() diff --git a/dashboard/src/account/tests/__init__.py b/dashboard/src/account/tests/__init__.py index b5914ce..b6fef6c 100644 --- a/dashboard/src/account/tests/__init__.py +++ b/dashboard/src/account/tests/__init__.py @@ -6,5 +6,3 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## - - diff --git a/dashboard/src/account/tests/test_general.py b/dashboard/src/account/tests/test_general.py index e8f483b..3fb52b0 100644 --- a/dashboard/src/account/tests/test_general.py +++ b/dashboard/src/account/tests/test_general.py @@ -30,7 +30,7 @@ class AccountMiddlewareTestCase(TestCase): The timezone should be UTC for anonymous users, for authenticated users it should be set to user.userprofile.timezone """ - #default + # default self.assertEqual(timezone.get_current_timezone_name(), 'UTC') url = reverse('account:settings') @@ -47,7 +47,7 @@ class AccountMiddlewareTestCase(TestCase): self.user1profile.timezone = 'Etc/Greenwich' self.user1profile.save() self.client.get(url) - self.assertEqual(timezone.get_current_timezone_name(), 'Etc/Greenwich') + self.assertEqual(timezone.get_current_timezone_name(), 'GMT') # if there is no profile for a user, it should be created user2 = User.objects.create(username='user2') @@ -56,5 +56,3 @@ class AccountMiddlewareTestCase(TestCase): self.client.login(username='user2', password='user2') self.client.get(url) self.assertTrue(user2.userprofile) - - diff --git a/dashboard/src/account/urls.py b/dashboard/src/account/urls.py index 3962a0c..8aad80c 100644 --- a/dashboard/src/account/urls.py +++ b/dashboard/src/account/urls.py @@ -1,5 +1,6 @@ ############################################################################## # Copyright (c) 2016 Max Breitenfeldt and others. +# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 @@ -24,13 +25,39 @@ Including another URLconf 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url +from django.urls import path -from account.views import * +from account.views import ( + AccountSettingsView, + JiraAuthenticatedView, + JiraLoginView, + JiraLogoutView, + UserListView, + account_resource_view, + account_booking_view, + account_images_view, + account_configuration_view, + account_detail_view, + resource_delete_view, + booking_cancel_view, + image_delete_view, + configuration_delete_view +) +app_name = "account" urlpatterns = [ url(r'^settings/', AccountSettingsView.as_view(), name='settings'), url(r'^authenticated/$', JiraAuthenticatedView.as_view(), name='authenticated'), url(r'^login/$', JiraLoginView.as_view(), name='login'), url(r'^logout/$', JiraLogoutView.as_view(), name='logout'), url(r'^users/$', UserListView.as_view(), name='users'), + url(r'^my/resources/$', account_resource_view, name="my-resources"), + path('my/resources/delete/<int:resource_id>', resource_delete_view), + url(r'^my/bookings/$', account_booking_view, name="my-bookings"), + path('my/bookings/cancel/<int:booking_id>', booking_cancel_view), + url(r'^my/images/$', account_images_view, name="my-images"), + path('my/images/delete/<int:image_id>', image_delete_view), + url(r'^my/configurations/$', account_configuration_view, name="my-configurations"), + path('my/configurations/delete/<int:config_id>', configuration_delete_view), + url(r'^my/$', account_detail_view, name="my-account"), ] diff --git a/dashboard/src/account/views.py b/dashboard/src/account/views.py index e6a0e5d..2b4eccb 100644 --- a/dashboard/src/account/views.py +++ b/dashboard/src/account/views.py @@ -1,5 +1,6 @@ ############################################################################## # Copyright (c) 2016 Max Breitenfeldt and others. +# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 @@ -13,20 +14,26 @@ import urllib import oauth2 as oauth from django.conf import settings +from django.utils import timezone from django.contrib import messages from django.contrib.auth import logout, authenticate, login from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.urls import reverse +from django.http import HttpResponse +from django.shortcuts import get_object_or_404 from django.utils.decorators import method_decorator from django.views.generic import RedirectView, TemplateView, UpdateView +from django.shortcuts import render from jira import JIRA from rest_framework.authtoken.models import Token from account.forms import AccountSettingsForm from account.jira_util import SignatureMethod_RSA_SHA1 from account.models import UserProfile +from booking.models import Booking +from resource_inventory.models import GenericResourceBundle, ConfigBundle, Image, Host @method_decorator(login_required, name='dispatch') @@ -59,7 +66,7 @@ class JiraLoginView(RedirectView): # Step 1. Get a request token from Jira. try: resp, content = client.request(settings.OAUTH_REQUEST_TOKEN_URL, "POST") - except Exception as e: + except Exception: messages.add_message(self.request, messages.ERROR, 'Error: Connection to Jira failed. Please contact an Administrator') return '/' @@ -72,8 +79,8 @@ class JiraLoginView(RedirectView): self.request.session['request_token'] = dict(urllib.parse.parse_qsl(content.decode())) # Step 3. Redirect the user to the authentication URL. url = settings.OAUTH_AUTHORIZE_URL + '?oauth_token=' + \ - self.request.session['request_token']['oauth_token'] + \ - '&oauth_callback=' + settings.OAUTH_CALLBACK_URL + self.request.session['request_token']['oauth_token'] + \ + '&oauth_callback=' + settings.OAUTH_CALLBACK_URL return url @@ -95,7 +102,7 @@ class JiraAuthenticatedView(RedirectView): # Step 2. Request the authorized access token from Jira. try: resp, content = client.request(settings.OAUTH_ACCESS_TOKEN_URL, "POST") - except Exception as e: + except Exception: messages.add_message(self.request, messages.ERROR, 'Error: Connection to Jira failed. Please contact an Administrator') return '/' @@ -153,3 +160,133 @@ class UserListView(TemplateView): context = super(UserListView, self).get_context_data(**kwargs) context.update({'title': "Dashboard Users", 'users': users}) return context + + +def account_detail_view(request): + template = "account/details.html" + return render(request, template) + + +def account_resource_view(request): + """ + gathers a users genericResoureBundles and + turns them into displayable objects + """ + if not request.user.is_authenticated: + return render(request, "dashboard/login.html", {'title': 'Authentication Required'}) + template = "account/resource_list.html" + resources = GenericResourceBundle.objects.filter( + owner=request.user).prefetch_related("configbundle_set") + mapping = {} + resource_list = [] + booking_mapping = {} + for grb in resources: + resource_list.append(grb) + mapping[grb.id] = [{"id": x.id, "name": x.name} for x in grb.configbundle_set.all()] + if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists(): + booking_mapping[grb.id] = "true" + context = { + "resources": resource_list, + "grb_mapping": mapping, + "booking_mapping": booking_mapping, + "title": "My Resources" + } + return render(request, template, context=context) + + +def account_booking_view(request): + if not request.user.is_authenticated: + return render(request, "dashboard/login.html", {'title': 'Authentication Required'}) + template = "account/booking_list.html" + bookings = list(Booking.objects.filter(owner=request.user, end__gt=timezone.now()).order_by("-start")) + my_old_bookings = Booking.objects.filter(owner=request.user, end__lt=timezone.now()).order_by("-start") + collab_old_bookings = request.user.collaborators.filter(end__lt=timezone.now()).order_by("-start") + expired_bookings = list(my_old_bookings.union(collab_old_bookings)) + collab_bookings = list(request.user.collaborators.filter(end__gt=timezone.now()).order_by("-start")) + context = { + "title": "My Bookings", + "bookings": bookings, + "collab_bookings": collab_bookings, + "expired_bookings": expired_bookings + } + return render(request, template, context=context) + + +def account_configuration_view(request): + if not request.user.is_authenticated: + return render(request, "dashboard/login.html", {'title': 'Authentication Required'}) + template = "account/configuration_list.html" + configs = list(ConfigBundle.objects.filter(owner=request.user)) + context = {"title": "Configuration List", "configurations": configs} + return render(request, template, context=context) + + +def account_images_view(request): + if not request.user.is_authenticated: + return render(request, "dashboard/login.html", {'title': 'Authentication Required'}) + template = "account/image_list.html" + my_images = Image.objects.filter(owner=request.user) + public_images = Image.objects.filter(public=True) + used_images = {} + for image in my_images: + if Host.objects.filter(booked=True, config__image=image).exists(): + used_images[image.id] = "true" + context = { + "title": "Images", + "images": my_images, + "public_images": public_images, + "used_images": used_images + } + return render(request, template, context=context) + + +def resource_delete_view(request, resource_id=None): + if not request.user.is_authenticated: + return HttpResponse('no') # 403? + grb = get_object_or_404(GenericResourceBundle, pk=resource_id) + if not request.user.id == grb.owner.id: + return HttpResponse('no') # 403? + if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists(): + return HttpResponse('no') # 403? + grb.delete() + return HttpResponse('') + + +def configuration_delete_view(request, config_id=None): + if not request.user.is_authenticated: + return HttpResponse('no') # 403? + config = get_object_or_404(ConfigBundle, pk=config_id) + if not request.user.id == config.owner.id: + return HttpResponse('no') # 403? + if Booking.objects.filter(config_bundle=config, end__gt=timezone.now()).exists(): + return HttpResponse('no') + config.delete() + return HttpResponse('') + + +def booking_cancel_view(request, booking_id=None): + if not request.user.is_authenticated: + return HttpResponse('no') # 403? + booking = get_object_or_404(Booking, pk=booking_id) + if not request.user.id == booking.owner.id: + return HttpResponse('no') # 403? + + if booking.end < timezone.now(): # booking already over + return HttpResponse('') + + booking.end = timezone.now() + booking.save() + return HttpResponse('') + + +def image_delete_view(request, image_id=None): + if not request.user.is_authenticated: + return HttpResponse('no') # 403? + image = get_object_or_404(Image, pk=image_id) + if image.public or image.owner.id != request.user.id: + return HttpResponse('no') # 403? + # check if used in booking + if Host.objects.filter(booked=True, config__image=image).exists(): + return HttpResponse('no') # 403? + image.delete() + return HttpResponse('') |