removal of jira 55/72855/4
authorJacob Hodgdon <jhodgdon@iol.unh.edu>
Tue, 24 Aug 2021 19:48:36 +0000 (15:48 -0400)
committerRaven Hodgdon <jhodgdon@iol.unh.edu>
Tue, 14 Sep 2021 20:57:19 +0000 (16:57 -0400)
Change-Id: I1b1da49147219d94837cdd50b908ba30f8cf961c
Signed-off-by: Jacob Hodgdon <jhodgdon@iol.unh.edu>
Signed-off-by: Sawyer Bergeron <sbergeron@iol.unh.edu>
Signed-off-by: Raven Hodgdon <jhodgdon@iol.unh.edu>
29 files changed:
config.env.sample
config/rabbitmq/rabbitmq.conf [new file with mode: 0644]
docker-compose.yml
requirements.txt
src/account/jira_util.py [deleted file]
src/account/models.py
src/account/tasks.py [deleted file]
src/account/urls.py
src/account/views.py
src/api/views.py
src/booking/quick_deployer.py
src/booking/stats.py
src/booking/urls.py
src/booking/views.py
src/dashboard/templatetags/jira_filters.py [deleted file]
src/dashboard/urls.py
src/laas_dashboard/settings.py
src/notifier/urls.py
src/resource_inventory/urls.py
src/static/package-lock.json
src/templates/base/account/configuration_list.html [deleted file]
src/templates/base/account/details.html
src/templates/base/base.html
src/templates/base/booking/booking_delete.html
src/templates/base/booking/booking_table.html
src/templates/lfedge/booking/booking_table.html
src/workflow/models.py
web/Dockerfile
worker/Dockerfile

index 5b34217..c47f2bf 100644 (file)
@@ -34,13 +34,6 @@ SECRET_KEY=http://www.miniwebtool.com/django-secret-key-generator/
 OAUTH_CONSUMER_KEY=sample_key
 OAUTH_CONSUMER_SECRET=sample_secret
 
-# access information for Jira
-# In addition to this, the rsa keys from your jira admin
-# need to go into src/account
-JIRA_URL=sample_url
-JIRA_USER_NAME=sample_jira_user
-JIRA_USER_PASSWORD=sample_jira_pass
-
 # LFID
 OIDC_CLIENT_ID=sample_id
 OIDC_CLIENT_SECRET=sample_secret
@@ -55,8 +48,8 @@ OIDC_RP_SIGN_ALGO=RS256
 OIDC_OP_JWKS_ENDPOINT=https://sso.linuxfoundation.org/.well-known/jwks.json
 
 # Rabbitmq
-RABBITMQ_DEFAULT_USER=opnfv
-RABBITMQ_DEFAULT_PASS=opnfvopnfv
+DEFAULT_USER=opnfv
+DEFAULT_PASS=opnfvopnfv
 
 # Jenkins Build Server
 JENKINS_URL=https://build.opnfv.org/ci
diff --git a/config/rabbitmq/rabbitmq.conf b/config/rabbitmq/rabbitmq.conf
new file mode 100644 (file)
index 0000000..39c222c
--- /dev/null
@@ -0,0 +1,2 @@
+default_user=opnfv
+default_pass=opnfvopnfv
index ee8de2c..f0de7b2 100644 (file)
@@ -50,7 +50,9 @@ services:
         restart: always
         image: rabbitmq
         container_name: rm01
-        env_file: config.env
+          #env_file: config.env
+        volumes:
+          - ./config/rabbitmq:/etc/rabbitmq
         ports:
             - "5672:5672"
 
index b34dd1e..72afbfa 100644 (file)
@@ -1,18 +1,17 @@
-celery==3.1.23
-cryptography==2.6.1
+celery==5.1.2
+cryptography==3.4.7
 Django==2.2
 django-bootstrap4==0.0.8
 django-filter==2.0.0
 djangorestframework==3.8.2
-gunicorn==19.6.0
-jira==1.0.7
+gunicorn==20.1.0
 oauth2==1.9.0.post1
-oauthlib==1.1.2
-pika==0.10.0
-psycopg2==2.8.4
-PyJWT==1.4.2
-requests==2.22.0
+oauthlib==3.1.1
+pika==1.2.0
+psycopg2==2.8.6
+PyJWT==2.1.0
+requests==2.26.0
 django-fernet-fields==0.6
-pyyaml==3.13
-pytz==2018.5
-mozilla-django-oidc==1.2.3
+pyyaml==5.4.1
+pytz==2021.1
+mozilla-django-oidc==2.0.0
\ No newline at end of file
diff --git a/src/account/jira_util.py b/src/account/jira_util.py
deleted file mode 100644 (file)
index a522594..0000000
+++ /dev/null
@@ -1,65 +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
-##############################################################################
-
-
-import base64
-import os
-
-import oauth2 as oauth
-from django.conf import settings
-from jira import JIRA
-from tlslite.utils import keyfactory
-
-
-class SignatureMethod_RSA_SHA1(oauth.SignatureMethod):
-    name = 'RSA-SHA1'
-
-    def signing_base(self, request, consumer, token):
-        if not hasattr(request, 'normalized_url') or request.normalized_url is None:
-            raise ValueError("Base URL for request is not set.")
-
-        sig = (
-            oauth.escape(request.method),
-            oauth.escape(request.normalized_url),
-            oauth.escape(request.get_normalized_parameters()),
-        )
-
-        key = '%s&' % oauth.escape(consumer.secret)
-        if token:
-            key += oauth.escape(token.secret)
-        raw = '&'.join(sig)
-        return key, raw
-
-    def sign(self, request, consumer, token):
-        """Build the base signature string."""
-        key, raw = self.signing_base(request, consumer, token)
-
-        module_dir = os.path.dirname(__file__)  # get current directory
-        with open(module_dir + '/rsa.pem', 'r') as f:
-            data = f.read()
-        privateKeyString = data.strip()
-        privatekey = keyfactory.parsePrivateKey(privateKeyString)
-        raw = str.encode(raw)
-        signature = privatekey.hashAndSign(raw)
-        return base64.b64encode(signature)
-
-
-def get_jira(user):
-    module_dir = os.path.dirname(__file__)  # get current directory
-    with open(module_dir + '/rsa.pem', 'r') as f:
-        key_cert = f.read()
-
-    oauth_dict = {
-        'access_token': user.userprofile.oauth_token,
-        'access_token_secret': user.userprofile.oauth_secret,
-        'consumer_key': settings.OAUTH_CONSUMER_KEY,
-        'key_cert': key_cert
-    }
-
-    return JIRA(server=settings.JIRA_URL, oauth=oauth_dict)
index b71f0ac..210025e 100644 (file)
@@ -51,6 +51,7 @@ class UserProfile(models.Model):
     oauth_secret = models.CharField(max_length=1024, blank=False)
 
     jira_url = models.CharField(max_length=100, null=True, blank=True, default='')
+
     full_name = models.CharField(max_length=100, null=True, blank=True, default='')
     booking_privledge = models.BooleanField(default=False)
 
diff --git a/src/account/tasks.py b/src/account/tasks.py
deleted file mode 100644 (file)
index df98c73..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-##############################################################################
-# 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
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
-from celery import shared_task
-from django.contrib.auth.models import User
-from jira import JIRAError
-
-from account.jira_util import get_jira
-
-
-@shared_task
-def sync_jira_accounts():
-    users = User.objects.all()
-    for user in users:
-        jira = get_jira(user)
-        try:
-            user_dict = jira.myself()
-        except JIRAError:
-            # User can be anonymous (local django admin account)
-            continue
-        try:
-            user.email = user_dict['emailAddress']
-        except KeyError:
-            pass
-        user.userprofile.url = user_dict['self']
-        user.userprofile.full_name = user_dict['displayName']
-
-        user.userprofile.save()
-        user.save()
index 97d8c77..804c133 100644 (file)
@@ -30,46 +30,30 @@ from django.urls import path
 
 from account.views import (
     AccountSettingsView,
-    JiraAuthenticatedView,
-    JiraLoginView,
     OIDCLoginView,
-    JiraLogoutView,
+    LogoutView,
     UserListView,
     account_resource_view,
     account_booking_view,
     account_images_view,
-    account_configuration_view,
     account_detail_view,
-    resource_delete_view,
+    template_delete_view,
     booking_cancel_view,
     image_delete_view,
-    configuration_delete_view
 )
 
-from laas_dashboard import settings
+app_name = 'account'
 
-
-def get_login_view():
-    if (settings.AUTH_SETTING == 'LFID'):
-        return OIDCLoginView.as_view()
-    else:
-        return JiraLoginView.as_view()
-
-
-app_name = "account"
 urlpatterns = [
     url(r'^settings/', AccountSettingsView.as_view(), name='settings'),
-    url(r'^authenticated/$', JiraAuthenticatedView.as_view(), name='authenticated'),
-    url(r'^login/$', get_login_view(), name='login'),
-    url(r'^logout/$', JiraLogoutView.as_view(), name='logout'),
+    url(r'^login/$', OIDCLoginView.as_view(), name='login'),
+    url(r'^logout/$', LogoutView.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"),
+    url(r'^my/resources/$', account_resource_view, name='my-resources'),
+    path('my/resources/delete/<int:resource_id>', template_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"),
+    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"),
-]
+    url(r'^my/$', account_detail_view, name='my-account'),
+]
\ No newline at end of file
index b74126e..167b5c4 100644 (file)
@@ -26,13 +26,11 @@ 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 mozilla_django_oidc.auth import OIDCAuthenticationBackend
 
 
 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 ResourceTemplate, Image
@@ -69,7 +67,7 @@ class MyOIDCAB(OIDCAuthenticationBackend):
         If this changes we will need to match users based on some
         other criterea.
         """
-        username = claims.get(os.environ['CLAIMS_ENDPOINT'] + 'username')
+        username = claims.get(os.environ.get('CLAIMS_ENDPOINT') + 'username')
 
         if not username:
             return HttpResponse('No username provided, contact support.')
@@ -101,109 +99,17 @@ class MyOIDCAB(OIDCAuthenticationBackend):
         return user
 
 
-class JiraLoginView(RedirectView):
-    def get_redirect_url(self, *args, **kwargs):
-        consumer = oauth.Consumer(settings.OAUTH_CONSUMER_KEY, settings.OAUTH_CONSUMER_SECRET)
-        client = oauth.Client(consumer)
-        client.set_signature_method(SignatureMethod_RSA_SHA1())
-
-        # Step 1. Get a request token from Jira.
-        try:
-            resp, content = client.request(settings.OAUTH_REQUEST_TOKEN_URL, "POST")
-        except Exception:
-            messages.add_message(self.request, messages.ERROR,
-                                 'Error: Connection to Jira failed. Please contact an Administrator')
-            return '/'
-        if resp['status'] != '200':
-            messages.add_message(self.request, messages.ERROR,
-                                 'Error: Connection to Jira failed. Please contact an Administrator')
-            return '/'
-
-        # Step 2. Store the request token in a session for later use.
-        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
-        return url
-
-
 class OIDCLoginView(RedirectView):
     def get_redirect_url(self, *args, **kwargs):
         return reverse('oidc_authentication_init')
 
 
-class JiraLogoutView(LoginRequiredMixin, RedirectView):
+class LogoutView(LoginRequiredMixin, RedirectView):
     def get_redirect_url(self, *args, **kwargs):
         logout(self.request)
         return '/'
 
 
-class JiraAuthenticatedView(RedirectView):
-    def get_redirect_url(self, *args, **kwargs):
-        # Step 1. Use the request token in the session to build a new client.
-        consumer = oauth.Consumer(settings.OAUTH_CONSUMER_KEY, settings.OAUTH_CONSUMER_SECRET)
-        token = oauth.Token(self.request.session['request_token']['oauth_token'],
-                            self.request.session['request_token']['oauth_token_secret'])
-        client = oauth.Client(consumer, token)
-        client.set_signature_method(SignatureMethod_RSA_SHA1())
-
-        # Step 2. Request the authorized access token from Jira.
-        try:
-            resp, content = client.request(settings.OAUTH_ACCESS_TOKEN_URL, "POST")
-        except Exception:
-            messages.add_message(self.request, messages.ERROR,
-                                 'Error: Connection to Jira failed. Please contact an Administrator')
-            return '/'
-        if resp['status'] != '200':
-            messages.add_message(self.request, messages.ERROR,
-                                 'Error: Connection to Jira failed. Please contact an Administrator')
-            return '/'
-
-        access_token = dict(urllib.parse.parse_qsl(content.decode()))
-
-        module_dir = os.path.dirname(__file__)  # get current directory
-        with open(module_dir + '/rsa.pem', 'r') as f:
-            key_cert = f.read()
-
-        oauth_dict = {
-            'access_token': access_token['oauth_token'],
-            'access_token_secret': access_token['oauth_token_secret'],
-            'consumer_key': settings.OAUTH_CONSUMER_KEY,
-            'key_cert': key_cert
-        }
-
-        jira = JIRA(server=settings.JIRA_URL, oauth=oauth_dict)
-        username = jira.current_user()
-        email = ""
-        try:
-            email = jira.user(username).emailAddress
-        except AttributeError:
-            email = ""
-        url = '/'
-        # Step 3. Lookup the user or create them if they don't exist.
-        try:
-            user = User.objects.get(username=username)
-        except User.DoesNotExist:
-            # Save our permanent token and secret for later.
-            user = User.objects.create_user(username=username,
-                                            password=access_token['oauth_token_secret'])
-            profile = UserProfile()
-            profile.user = user
-            profile.save()
-            user.userprofile.email_addr = email
-            url = reverse('account:settings')
-        user.userprofile.oauth_token = access_token['oauth_token']
-        user.userprofile.oauth_secret = access_token['oauth_token_secret']
-        user.userprofile.save()
-        user.set_password(access_token['oauth_token_secret'])
-        user.save()
-        user = authenticate(username=username, password=access_token['oauth_token_secret'])
-        login(self.request, user)
-        # redirect user to settings page to complete profile
-        return url
-
-
 @method_decorator(login_required, name='dispatch')
 class UserListView(TemplateView):
     template_name = "account/user_list.html"
@@ -232,9 +138,9 @@ def account_resource_view(request):
     template = "account/resource_list.html"
 
     active_bundles = [book.resource for book in Booking.objects.filter(
-        owner=request.user, end__gte=timezone.now())]
+        owner=request.user, end__gte=timezone.now(), resource__template__temporary=False)]
     active_resources = [bundle.template.id for bundle in active_bundles]
-    resource_list = list(ResourceTemplate.objects.filter(owner=request.user))
+    resource_list = list(ResourceTemplate.objects.filter(owner=request.user, temporary=False))
 
     context = {
         "resources": resource_list,
@@ -262,14 +168,6 @@ def account_booking_view(request):
     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(ResourceTemplate.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:
@@ -290,28 +188,19 @@ def account_images_view(request):
     return render(request, template, context=context)
 
 
-def resource_delete_view(request, resource_id=None):
+def template_delete_view(request, resource_id=None):
     if not request.user.is_authenticated:
-        return HttpResponse('no')  # 403?
-    grb = get_object_or_404(ResourceTemplate, 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(ResourceTemplate, pk=config_id)
-    if not request.user.id == config.owner.id:
-        return HttpResponse('no')  # 403?
-    if Booking.objects.filter(resource__template=config, end__gt=timezone.now()).exists():
-        return HttpResponse('no')
-    config.delete()
-    return HttpResponse('')
+        return HttpResponse(status=403)
+    template = get_object_or_404(ResourceTemplate, pk=resource_id)
+    if not request.user.id == template.owner.id:
+        return HttpResponse(status=403)
+    if Booking.objects.filter(resource__template=template, end__gt=timezone.now()).exists():
+        return HttpResponse(status=403)
+    #grb.delete()
+    template.public = False
+    template.temporary = True
+    template.save()
+    return HttpResponse(status=200)
 
 
 def booking_cancel_view(request, booking_id=None):
index 1793c79..c0da1bc 100644 (file)
@@ -391,7 +391,7 @@ def available_templates(request):
     # mirrors MultipleSelectFilter Widget
     avt = []
     for lab in Lab.objects.all():
-        for template in ResourceTemplate.objects.filter(Q(lab=lab), Q(owner=token.user) | Q(public=True)):
+        for template in ResourceTemplate.objects.filter(Q(owner=token.user) | Q(public=True), lab=lab, temporary=False):
             available_resources = lab.get_available_resources()
             required_resources = template.get_required_resources()
             least_available = 100
index 5e5bc8b..9806348 100644 (file)
@@ -227,7 +227,6 @@ def create_from_API(body, user):
     data['scenario'] = None
 
     data['image'] = Image.objects.get(pk=booking_info['imageLabID'])
-
     data['resource_template'] = ResourceTemplate.objects.get(pk=booking_info['templateID'])
     data['lab'] = data['resource_template'].lab
     data['owner'] = user
index 626ed79..70f91fa 100644 (file)
@@ -104,5 +104,5 @@ class StatisticsManager(object):
             "user": [x, users],
             "utils": [in_use, not_in_use, maintenance],
             "projects": [project_keys, project_counts],
-            "colors": anuket_colors if os.environ['TEMPLATE_OVERRIDE_DIR'] == 'laas' else lfedge_colors
+            "colors": anuket_colors if os.environ.get('TEMPLATE_OVERRIDE_DIR') == 'laas' else lfedge_colors
         }
index cdf18ae..0b60351 100644 (file)
@@ -38,7 +38,7 @@ from booking.views import (
     booking_modify_image
 )
 
-app_name = "booking"
+app_name = 'booking'
 urlpatterns = [
     url(r'^detail/(?P<booking_id>[0-9]+)/$', booking_detail_view, name='detail'),
     url(r'^(?P<booking_id>[0-9]+)/$', booking_detail_view, name='booking_detail'),
index 2b910e7..a418c82 100644 (file)
@@ -127,7 +127,6 @@ class ResourceBookingsJSON(View):
             'start',
             'end',
             'purpose',
-            'jira_issue_status',
             'config_bundle__name'
         )
         return JsonResponse({'bookings': list(bookings)})
diff --git a/src/dashboard/templatetags/jira_filters.py b/src/dashboard/templatetags/jira_filters.py
deleted file mode 100644 (file)
index 9a97c1d..0000000
+++ /dev/null
@@ -1,17 +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
-##############################################################################
-
-
-from django.conf import settings
-from django.template.defaultfilters import register
-
-
-@register.filter
-def jira_issue_url(issue):
-    return settings.JIRA_URL + '/browse/' + str(issue)
index d5dad57..c87dacc 100644 (file)
@@ -33,7 +33,7 @@ from dashboard.views import (
     host_profile_detail_view
 )
 
-app_name = "dashboard"
+app_name = 'dashboard'
 urlpatterns = [
     url(r'^$', landing_view, name='index'),
     url(r'^lab/$', lab_list_view, name='all_labs'),
index 6b3ed09..010ffde 100644 (file)
@@ -15,8 +15,8 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 # SECURITY WARNING: don't run with debug turned on in production!
 # NOTE: os.environ only returns strings, so making a comparison to
 # 'True' here will convert it to the correct Boolean value.
-DEBUG = os.environ['DEBUG'] == 'True'
-TESTING = os.environ['TEST'] == 'True'
+DEBUG = os.environ.get('DEBUG') == 'True'
+TESTING = os.environ.get('TEST') == 'True'
 
 # Application definition
 
@@ -53,29 +53,35 @@ MIDDLEWARE = [
     'account.middleware.TimezoneMiddleware',
 ]
 
-AUTH_SETTING = os.environ.get('AUTH_SETTING', 'JIRA')
 
-if AUTH_SETTING == 'LFID':
-    AUTHENTICATION_BACKENDS = ['account.views.MyOIDCAB']
+#AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend', 'account.views.MyOIDCAB']
+AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']
+
+AUTH_SETTING = os.environ.get('AUTH_SETTING')
 
+if AUTH_SETTING == 'LFID':
     # OpenID Authentications
-    OIDC_RP_CLIENT_ID = os.environ['OIDC_CLIENT_ID']
-    OIDC_RP_CLIENT_SECRET = os.environ['OIDC_CLIENT_SECRET']
+    AUTHENTICATION_BACKENDS.append('account.views.MyOIDCAB')
+    OIDC_RP_CLIENT_ID = os.environ.get('OIDC_CLIENT_ID')
+    OIDC_RP_CLIENT_SECRET = os.environ.get('OIDC_CLIENT_SECRET')
 
-    OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ['OIDC_AUTHORIZATION_ENDPOINT']
-    OIDC_OP_TOKEN_ENDPOINT = os.environ['OIDC_TOKEN_ENDPOINT']
-    OIDC_OP_USER_ENDPOINT = os.environ['OIDC_USER_ENDPOINT']
+    OIDC_OP_AUTHORIZATION_ENDPOINT = os.environ.get('OIDC_AUTHORIZATION_ENDPOINT')
+    OIDC_OP_TOKEN_ENDPOINT = os.environ.get('OIDC_TOKEN_ENDPOINT')
+    OIDC_OP_USER_ENDPOINT = os.environ.get('OIDC_USER_ENDPOINT')
 
-    LOGIN_REDIRECT_URL = os.environ['DASHBOARD_URL']
-    LOGOUT_REDIRECT_URL = os.environ['DASHBOARD_URL']
+    LOGIN_REDIRECT_URL = os.environ.get('DASHBOARD_URL')
+    LOGOUT_REDIRECT_URL = os.environ.get('DASHBOARD_URL')
 
-    OIDC_RP_SIGN_ALGO = os.environ["OIDC_RP_SIGN_ALGO"]
+    OIDC_RP_SIGN_ALGO = os.environ.get("OIDC_RP_SIGN_ALGO")
+    #raise Exception("OIDC rp sign algo is: ", OIDC_RP_SIGN_ALGO)
 
     if OIDC_RP_SIGN_ALGO == "RS256":
-        OIDC_OP_JWKS_ENDPOINT = os.environ["OIDC_OP_JWKS_ENDPOINT"]
+        OIDC_OP_JWKS_ENDPOINT = os.environ.get("OIDC_OP_JWKS_ENDPOINT")
+else:
+    raise Exception('AUTH_SETTING set to invalid value')
 
 # This is for LFID auth setups w/ an HTTPS proxy
-if os.environ['EXPECT_HOST_FORWARDING'] == 'True':
+if os.environ.get('EXPECT_HOST_FORWARDING') == 'True':
     SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', "https")
     USE_X_FORWARDED_HOST = True
 
@@ -162,7 +168,7 @@ STATICFILES_DIRS = [
 LOGIN_REDIRECT_URL = '/'
 
 # SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = os.environ['SECRET_KEY']
+SECRET_KEY = os.environ.get('SECRET_KEY')
 
 BOOTSTRAP3 = {
     'set_placeholder': False,
@@ -175,11 +181,11 @@ ALLOWED_HOSTS = ['*']
 DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.postgresql',
-        'NAME': os.environ['DB_NAME'],
-        'USER': os.environ['DB_USER'],
-        'PASSWORD': os.environ['DB_PASS'],
-        'HOST': os.environ['DB_SERVICE'],
-        'PORT': os.environ['DB_PORT']
+        'NAME': os.environ.get('DB_NAME'),
+        'USER': os.environ.get('DB_USER'),
+        'PASSWORD': os.environ.get('DB_PASS'),
+        'HOST': os.environ.get('DB_SERVICE'),
+        'PORT': os.environ.get('DB_PORT')
     }
 }
 
@@ -198,27 +204,17 @@ REST_FRAMEWORK = {
 MEDIA_ROOT = '/media'
 STATIC_ROOT = '/static'
 
-# Jira Settings
-CREATE_JIRA_TICKET = False
-
-JIRA_URL = os.environ['JIRA_URL']
-
-JIRA_USER_NAME = os.environ['JIRA_USER_NAME']
-JIRA_USER_PASSWORD = os.environ['JIRA_USER_PASSWORD']
-
-OAUTH_CONSUMER_KEY = os.environ['OAUTH_CONSUMER_KEY']
-OAUTH_CONSUMER_SECRET = os.environ['OAUTH_CONSUMER_SECRET']
-
-OAUTH_REQUEST_TOKEN_URL = JIRA_URL + '/plugins/servlet/oauth/request-token'
-OAUTH_ACCESS_TOKEN_URL = JIRA_URL + '/plugins/servlet/oauth/access-token'
-OAUTH_AUTHORIZE_URL = JIRA_URL + '/plugins/servlet/oauth/authorize'
+OAUTH_CONSUMER_KEY = os.environ.get('OAUTH_CONSUMER_KEY')
+OAUTH_CONSUMER_SECRET = os.environ.get('OAUTH_CONSUMER_SECRET')
 
-OAUTH_CALLBACK_URL = os.environ['DASHBOARD_URL'] + '/accounts/authenticated'
+OAUTH_CALLBACK_URL = os.environ.get('DASHBOARD_URL') + '/accounts/authenticated'
 
 # Celery Settings
 CELERY_TIMEZONE = 'UTC'
 
 RABBITMQ_URL = 'rabbitmq'
+#RABBITMQ_DEFAULT_USER = os.environ['DEFAULT_USER']
+#RABBITMQ_DEFAULT_PASS = os.environ['DEFAULT_PASS']
 RABBITMQ_DEFAULT_USER = os.environ['RABBITMQ_DEFAULT_USER']
 RABBITMQ_DEFAULT_PASS = os.environ['RABBITMQ_DEFAULT_PASS']
 
@@ -248,10 +244,10 @@ CELERYBEAT_SCHEDULE = {
 }
 
 # Notifier Settings
-EMAIL_HOST = os.environ['EMAIL_HOST']
-EMAIL_PORT = os.environ['EMAIL_PORT']
-EMAIL_HOST_USER = os.environ['EMAIL_HOST_USER']
-EMAIL_HOST_PASSWORD = os.environ['EMAIL_HOST_PASSWORD']
+EMAIL_HOST = os.environ.get('EMAIL_HOST')
+EMAIL_PORT = os.environ.get('EMAIL_PORT')
+EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER')
+EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD')
 EMAIL_USE_TLS = True
 DEFAULT_EMAIL_FROM = os.environ.get('DEFAULT_EMAIL_FROM', 'webmaster@localhost')
 SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"
index fedb9e8..923cc33 100644 (file)
@@ -12,7 +12,7 @@ from django.conf.urls import url
 
 from notifier.views import InboxView, NotificationView
 
-app_name = "notifier"
+app_name = 'notifier'
 urlpatterns = [
     url(r'^$', InboxView, name='messages'),
     url(r'^notification/(?P<notification_id>[0-9]+)/$', NotificationView, name='notifier_single')
index a008176..a9a4d43 100644 (file)
@@ -29,7 +29,7 @@ from django.conf.urls import url
 from resource_inventory.views import HostView, hostprofile_detail_view
 
 
-app_name = "resource"
+app_name = 'resource'
 urlpatterns = [
     url(r'^hosts$', HostView.as_view(), name='hosts'),
     url(r'^profiles/(?P<hostprofile_id>.+)/$', hostprofile_detail_view, name='host_detail'),
index f8eabe4..89a26db 100644 (file)
@@ -1,8 +1,97 @@
 {
   "name": "laas",
   "version": "1.0.0",
-  "lockfileVersion": 1,
+  "lockfileVersion": 2,
   "requires": true,
+  "packages": {
+    "": {
+      "name": "laas",
+      "version": "1.0.0",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@fortawesome/fontawesome-free": "^5.12.0",
+        "bootstrap": "^4.4.1",
+        "datatables.net-bs4": "^1.10.20",
+        "datatables.net-responsive-bs4": "^2.2.3",
+        "jquery": "^3.4.1",
+        "mxgraph": "^4.0.6",
+        "plotly.js-dist": "^1.51.3",
+        "popper.js": "^1.16.0"
+      }
+    },
+    "node_modules/@fortawesome/fontawesome-free": {
+      "version": "5.12.0",
+      "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.12.0.tgz",
+      "integrity": "sha512-vKDJUuE2GAdBERaQWmmtsciAMzjwNrROXA5KTGSZvayAsmuTGjam5z6QNqNPCwDfVljLWuov1nEC3mEQf/n6fQ==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/bootstrap": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.4.1.tgz",
+      "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA==",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/datatables.net": {
+      "version": "1.10.20",
+      "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-1.10.20.tgz",
+      "integrity": "sha512-4E4S7tTU607N3h0fZPkGmAtr9mwy462u+VJ6gxYZ8MxcRIjZqHy3Dv1GNry7i3zQCktTdWbULVKBbkAJkuHEnQ==",
+      "dependencies": {
+        "jquery": "3.4.1"
+      }
+    },
+    "node_modules/datatables.net-bs4": {
+      "version": "1.10.20",
+      "resolved": "https://registry.npmjs.org/datatables.net-bs4/-/datatables.net-bs4-1.10.20.tgz",
+      "integrity": "sha512-kQmMUMsHMOlAW96ztdoFqjSbLnlGZQ63iIM82kHbmldsfYdzuyhbb4hTx6YNBi481WCO3iPSvI6YodNec46ZAw==",
+      "dependencies": {
+        "datatables.net": "1.10.20",
+        "jquery": "3.4.1"
+      }
+    },
+    "node_modules/datatables.net-responsive": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/datatables.net-responsive/-/datatables.net-responsive-2.2.3.tgz",
+      "integrity": "sha512-8D6VtZcyuH3FG0Hn5A4LPZQEOX3+HrRFM7HjpmsQc/nQDBbdeBLkJX4Sh/o1nzFTSneuT1Wh/lYZHVPpjcN+Sw==",
+      "dependencies": {
+        "datatables.net": "1.10.20",
+        "jquery": "3.4.1"
+      }
+    },
+    "node_modules/datatables.net-responsive-bs4": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/datatables.net-responsive-bs4/-/datatables.net-responsive-bs4-2.2.3.tgz",
+      "integrity": "sha512-SQaWI0uLuPcaiBBin9zX+MuQfTSIkK1bYxbXqUV6NLkHCVa6PMQK7Rvftj0ywG4R7uOtjbzY8nSVqxEKvQI0Vg==",
+      "dependencies": {
+        "datatables.net-bs4": "1.10.20",
+        "datatables.net-responsive": "2.2.3",
+        "jquery": "3.4.1"
+      }
+    },
+    "node_modules/jquery": {
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz",
+      "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw=="
+    },
+    "node_modules/mxgraph": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/mxgraph/-/mxgraph-4.0.6.tgz",
+      "integrity": "sha512-5XZXeAkA4k6n4BS05Fxd2cNhMw+3dnlRqAaLtsuXdT0g8BvvEa1VT4jjuGtUW4QTt38Q+I2Dr/3EWiAaGRfAXw=="
+    },
+    "node_modules/plotly.js-dist": {
+      "version": "1.51.3",
+      "resolved": "https://registry.npmjs.org/plotly.js-dist/-/plotly.js-dist-1.51.3.tgz",
+      "integrity": "sha512-Bxz0XBg963gpnbt7FVPEhYvT33JsaKa0hEozXBnQZkiKtsiM2M1lZN6tkEHmq6o1N2K6qJXFtdzCXbZ/hLGV0Q=="
+    },
+    "node_modules/popper.js": {
+      "version": "1.16.0",
+      "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.0.tgz",
+      "integrity": "sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw=="
+    }
+  },
   "dependencies": {
     "@fortawesome/fontawesome-free": {
       "version": "5.12.0",
diff --git a/src/templates/base/account/configuration_list.html b/src/templates/base/account/configuration_list.html
deleted file mode 100644 (file)
index fee6e83..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-{% extends "base.html" %}
-{% block content %}
-<div class="row">
-{% for config in configurations %}
-    <div class="col-12 col-md-6 col-lg-4 col-xl-3 mb-3">
-        <div class="card h-100">
-            <div class="card-header">
-                <h3>Configuration {{config.id}}</h3>
-            </div>
-            <ul class="list-group list-group-flush h-100">
-                <li class="list-group-item">id: {{config.id}}</li>
-                <li class="list-group-item">name: {{config.name}}</li>
-                <li class="list-group-item">description: {{config.description}}</li>
-                <li class="list-group-item">resource: {{config.bundle}}</li>
-            </ul>
-            <div class="card-footer">
-                <button
-                    class="btn btn-danger w-100"
-                    onclick='delete_config({{config.id}});'
-                    data-toggle="modal"
-                    data-target="#configModal"
-                >Delete</button>
-            </div>
-        </div>
-    </div>
-{% empty %}
-    <div class="col">
-        <p>You don't have any configurations. You can create a configuration by configuring a pod.</p>
-    </div>
-{% endfor %}
-</div>
-
-<script>
-    var current_config_id = -1;
-    function delete_config(config_id) {
-        current_config_id = config_id;
-    }
-
-    function submit_delete_form() {
-        var ajaxForm = $("#config_delete_form");
-        var formData = ajaxForm.serialize();
-        req = new XMLHttpRequest();
-        var url = "delete/" + current_config_id;
-        req.onreadystatechange = function() {
-            if (this.readyState == 4 && this.status == 200) {
-                location.reload();
-            }
-        };
-        req.open("POST", url, true);
-        req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
-        req.onerror = function() { alert("problem submitting form"); }
-        req.send(formData);
-    }
-</script>
-
-<div class="modal fade" id="configModal" tabindex="-1" role="dialog" aria-hidden="true">
-    <div class="modal-dialog" role="document">
-        <div class="modal-content">
-            <div class="modal-header">
-                <h4 class="modal-title d-inline float-left">Delete Configuration?</h4>
-                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
-                    <span aria-hidden="true">&times;</span>
-                </button>
-            </div>
-            <form id="config_delete_form">
-                {% csrf_token %}
-            </form>
-            <div class="modal-footer d-flex flex-column">
-                <div class="mb-2">
-                    <button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
-                    <button type="button" class="btn btn-danger" data-toggle="collapse" data-target="#warning">Delete</button>
-                </div>
-                <div class="collapse w-100 text-center border-top" id="warning">
-                    <div class="p-4">
-                        <h3>Are You Sure?</h3>
-                        <p>This cannot be undone</p>
-                        <button class="btn btn-outline-secondary" data-dismiss="modal">Nevermind</button>
-                        <button class="btn btn-danger" data-dismiss="modal" onclick="submit_delete_form();">I'm Sure</button>
-                    </div>
-                </div>
-            </div>
-        </div>
-    </div>
-</div>
-{% endblock %}
index 3092ad0..ad59c9a 100644 (file)
@@ -4,6 +4,5 @@
 <h1>Account Details</h1>
 <a class="btn btn-primary" href="{% url 'account:my-resources' %}">My Resources</a>
 <a class="btn btn-primary" href="{% url 'account:my-bookings' %}">My Bookings</a>
-<a class="btn btn-primary" href="{% url 'account:my-configurations' %}">My Configurations</a>
 <a class="btn btn-primary" href="{% url 'account:my-images' %}">My Snapshots</a>
 {% endblock content %}
index 394ddec..f334a63 100644 (file)
@@ -87,7 +87,7 @@
                                 {% else %}
                                     <a href="{% url 'account:login' %}" class="text-dark dropdown-item">
                                         <i class="fas fa-sign-in-alt"></i>
-                                        Login with Jira
+                                        Login
                                     </a>
                                 {% endif %}
                             {% endif %}
                                 <a href="{% url 'account:my-bookings' %}" class="list-group-item list-group-item-action list-group-item-secondary">
                                     My Bookings
                                 </a>
-                                <a href="{% url 'account:my-configurations' %}" class="list-group-item list-group-item-action list-group-item-secondary">
-                                    My Configurations
-                                </a>
                                 <a href="{% url 'account:my-images' %}" class="list-group-item list-group-item-action list-group-item-secondary">
                                     My Snapshots
                                 </a>
index f550a38..91a216c 100644 (file)
@@ -160,7 +160,7 @@ class BookingAuthManager():
             return True  # admin override for this user
         if Booking.objects.filter(owner=booking.owner, end__gt=timezone.now()).count() >= 3:
             return False
-        if len(booking.resource.template.getResources()) < 2:
+        if len(booking.resource.template.get_required_resources()) < 2:
             return True  # if they only have one server, we dont care
         if repo.BOOKING_INFO_FILE not in repo.el:
             return False  # INFO file not provided
index 40ab8f0..c05193a 100644 (file)
@@ -6,7 +6,7 @@
 # which accompanies this distribution, and is available at
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
-FROM python:3.5
+FROM python:3.9
 ENV PYTHONUNBUFFERED 1
 
 RUN apt-get update && apt-get install -y npm
index 5e24bed..edf86d1 100644 (file)
@@ -6,7 +6,7 @@
 # which accompanies this distribution, and is available at
 # http://www.apache.org/licenses/LICENSE-2.0
 ##############################################################################
-FROM python:3.5
+FROM python:3.9
 ENV PYTHONUNBUFFERED 1
 
 ADD requirements.txt /requirements.txt