Hides expired bookings in the "My Bookings" Page
[pharos-tools.git] / dashboard / src / account / views.py
1 ##############################################################################
2 # Copyright (c) 2016 Max Breitenfeldt and others.
3 # Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
4 #
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ##############################################################################
10
11
12 import os
13 import urllib
14
15 import oauth2 as oauth
16 from django.conf import settings
17 from django.utils import timezone
18 from django.contrib import messages
19 from django.contrib.auth import logout, authenticate, login
20 from django.contrib.auth.decorators import login_required
21 from django.contrib.auth.mixins import LoginRequiredMixin
22 from django.contrib.auth.models import User
23 from django.urls import reverse
24 from django.http import HttpResponse
25 from django.shortcuts import get_object_or_404
26 from django.utils.decorators import method_decorator
27 from django.views.generic import RedirectView, TemplateView, UpdateView
28 from django.shortcuts import render
29 from jira import JIRA
30 from rest_framework.authtoken.models import Token
31
32 from account.forms import AccountSettingsForm
33 from account.jira_util import SignatureMethod_RSA_SHA1
34 from account.models import UserProfile
35 from booking.models import Booking
36 from resource_inventory.models import GenericResourceBundle, ConfigBundle, Image, Host
37
38
39 @method_decorator(login_required, name='dispatch')
40 class AccountSettingsView(UpdateView):
41     model = UserProfile
42     form_class = AccountSettingsForm
43     template_name_suffix = '_update_form'
44
45     def get_success_url(self):
46         messages.add_message(self.request, messages.INFO,
47                              'Settings saved')
48         return '/'
49
50     def get_object(self, queryset=None):
51         return self.request.user.userprofile
52
53     def get_context_data(self, **kwargs):
54         token, created = Token.objects.get_or_create(user=self.request.user)
55         context = super(AccountSettingsView, self).get_context_data(**kwargs)
56         context.update({'title': "Settings", 'token': token})
57         return context
58
59
60 class JiraLoginView(RedirectView):
61     def get_redirect_url(self, *args, **kwargs):
62         consumer = oauth.Consumer(settings.OAUTH_CONSUMER_KEY, settings.OAUTH_CONSUMER_SECRET)
63         client = oauth.Client(consumer)
64         client.set_signature_method(SignatureMethod_RSA_SHA1())
65
66         # Step 1. Get a request token from Jira.
67         try:
68             resp, content = client.request(settings.OAUTH_REQUEST_TOKEN_URL, "POST")
69         except Exception:
70             messages.add_message(self.request, messages.ERROR,
71                                  'Error: Connection to Jira failed. Please contact an Administrator')
72             return '/'
73         if resp['status'] != '200':
74             messages.add_message(self.request, messages.ERROR,
75                                  'Error: Connection to Jira failed. Please contact an Administrator')
76             return '/'
77
78         # Step 2. Store the request token in a session for later use.
79         self.request.session['request_token'] = dict(urllib.parse.parse_qsl(content.decode()))
80         # Step 3. Redirect the user to the authentication URL.
81         url = settings.OAUTH_AUTHORIZE_URL + '?oauth_token=' + \
82             self.request.session['request_token']['oauth_token'] + \
83             '&oauth_callback=' + settings.OAUTH_CALLBACK_URL
84         return url
85
86
87 class JiraLogoutView(LoginRequiredMixin, RedirectView):
88     def get_redirect_url(self, *args, **kwargs):
89         logout(self.request)
90         return '/'
91
92
93 class JiraAuthenticatedView(RedirectView):
94     def get_redirect_url(self, *args, **kwargs):
95         # Step 1. Use the request token in the session to build a new client.
96         consumer = oauth.Consumer(settings.OAUTH_CONSUMER_KEY, settings.OAUTH_CONSUMER_SECRET)
97         token = oauth.Token(self.request.session['request_token']['oauth_token'],
98                             self.request.session['request_token']['oauth_token_secret'])
99         client = oauth.Client(consumer, token)
100         client.set_signature_method(SignatureMethod_RSA_SHA1())
101
102         # Step 2. Request the authorized access token from Jira.
103         try:
104             resp, content = client.request(settings.OAUTH_ACCESS_TOKEN_URL, "POST")
105         except Exception:
106             messages.add_message(self.request, messages.ERROR,
107                                  'Error: Connection to Jira failed. Please contact an Administrator')
108             return '/'
109         if resp['status'] != '200':
110             messages.add_message(self.request, messages.ERROR,
111                                  'Error: Connection to Jira failed. Please contact an Administrator')
112             return '/'
113
114         access_token = dict(urllib.parse.parse_qsl(content.decode()))
115
116         module_dir = os.path.dirname(__file__)  # get current directory
117         with open(module_dir + '/rsa.pem', 'r') as f:
118             key_cert = f.read()
119
120         oauth_dict = {
121             'access_token': access_token['oauth_token'],
122             'access_token_secret': access_token['oauth_token_secret'],
123             'consumer_key': settings.OAUTH_CONSUMER_KEY,
124             'key_cert': key_cert
125         }
126
127         jira = JIRA(server=settings.JIRA_URL, oauth=oauth_dict)
128         username = jira.current_user()
129         email = jira.user(username).emailAddress
130         url = '/'
131         # Step 3. Lookup the user or create them if they don't exist.
132         try:
133             user = User.objects.get(username=username)
134         except User.DoesNotExist:
135             # Save our permanent token and secret for later.
136             user = User.objects.create_user(username=username,
137                                             password=access_token['oauth_token_secret'])
138             profile = UserProfile()
139             profile.user = user
140             profile.save()
141             user.userprofile.email_addr = email
142             url = reverse('account:settings')
143         user.userprofile.oauth_token = access_token['oauth_token']
144         user.userprofile.oauth_secret = access_token['oauth_token_secret']
145         user.userprofile.save()
146         user.set_password(access_token['oauth_token_secret'])
147         user.save()
148         user = authenticate(username=username, password=access_token['oauth_token_secret'])
149         login(self.request, user)
150         # redirect user to settings page to complete profile
151         return url
152
153
154 @method_decorator(login_required, name='dispatch')
155 class UserListView(TemplateView):
156     template_name = "account/user_list.html"
157
158     def get_context_data(self, **kwargs):
159         users = User.objects.all()
160         context = super(UserListView, self).get_context_data(**kwargs)
161         context.update({'title': "Dashboard Users", 'users': users})
162         return context
163
164
165 def account_detail_view(request):
166     template = "account/details.html"
167     return render(request, template)
168
169
170 def account_resource_view(request):
171     """
172     gathers a users genericResoureBundles and
173     turns them into displayable objects
174     """
175     if not request.user.is_authenticated:
176         return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
177     template = "account/resource_list.html"
178     resources = GenericResourceBundle.objects.filter(
179         owner=request.user).prefetch_related("configbundle_set")
180     mapping = {}
181     resource_list = []
182     booking_mapping = {}
183     for grb in resources:
184         resource_list.append(grb)
185         mapping[grb.id] = [{"id": x.id, "name": x.name} for x in grb.configbundle_set.all()]
186         if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists():
187             booking_mapping[grb.id] = "true"
188     context = {
189         "resources": resource_list,
190         "grb_mapping": mapping,
191         "booking_mapping": booking_mapping,
192         "title": "My Resources"
193     }
194     return render(request, template, context=context)
195
196
197 def account_booking_view(request):
198     if not request.user.is_authenticated:
199         return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
200     template = "account/booking_list.html"
201     bookings = list(Booking.objects.filter(owner=request.user, end__gt=timezone.now()).order_by("-start"))
202     my_old_bookings = Booking.objects.filter(owner=request.user, end__lt=timezone.now()).order_by("-start")
203     collab_old_bookings = request.user.collaborators.filter(end__lt=timezone.now()).order_by("-start")
204     expired_bookings = list(my_old_bookings.union(collab_old_bookings))
205     collab_bookings = list(request.user.collaborators.filter(end__gt=timezone.now()).order_by("-start"))
206     context = {
207         "title": "My Bookings",
208         "bookings": bookings,
209         "collab_bookings": collab_bookings,
210         "expired_bookings": expired_bookings
211     }
212     return render(request, template, context=context)
213
214
215 def account_configuration_view(request):
216     if not request.user.is_authenticated:
217         return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
218     template = "account/configuration_list.html"
219     configs = list(ConfigBundle.objects.filter(owner=request.user))
220     context = {"title": "Configuration List", "configurations": configs}
221     return render(request, template, context=context)
222
223
224 def account_images_view(request):
225     if not request.user.is_authenticated:
226         return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
227     template = "account/image_list.html"
228     my_images = Image.objects.filter(owner=request.user)
229     public_images = Image.objects.filter(public=True)
230     used_images = {}
231     for image in my_images:
232         if Host.objects.filter(booked=True, config__image=image).exists():
233             used_images[image.id] = "true"
234     context = {
235         "title": "Images",
236         "images": my_images,
237         "public_images": public_images,
238         "used_images": used_images
239     }
240     return render(request, template, context=context)
241
242
243 def resource_delete_view(request, resource_id=None):
244     if not request.user.is_authenticated:
245         return HttpResponse('no')  # 403?
246     grb = get_object_or_404(GenericResourceBundle, pk=resource_id)
247     if not request.user.id == grb.owner.id:
248         return HttpResponse('no')  # 403?
249     if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists():
250         return HttpResponse('no')  # 403?
251     grb.delete()
252     return HttpResponse('')
253
254
255 def configuration_delete_view(request, config_id=None):
256     if not request.user.is_authenticated:
257         return HttpResponse('no')  # 403?
258     config = get_object_or_404(ConfigBundle, pk=config_id)
259     if not request.user.id == config.owner.id:
260         return HttpResponse('no')  # 403?
261     if Booking.objects.filter(config_bundle=config, end__gt=timezone.now()).exists():
262         return HttpResponse('no')
263     config.delete()
264     return HttpResponse('')
265
266
267 def booking_cancel_view(request, booking_id=None):
268     if not request.user.is_authenticated:
269         return HttpResponse('no')  # 403?
270     booking = get_object_or_404(Booking, pk=booking_id)
271     if not request.user.id == booking.owner.id:
272         return HttpResponse('no')  # 403?
273
274     if booking.end < timezone.now():  # booking already over
275         return HttpResponse('')
276
277     booking.end = timezone.now()
278     booking.save()
279     return HttpResponse('')
280
281
282 def image_delete_view(request, image_id=None):
283     if not request.user.is_authenticated:
284         return HttpResponse('no')  # 403?
285     image = get_object_or_404(Image, pk=image_id)
286     if image.public or image.owner.id != request.user.id:
287         return HttpResponse('no')  # 403?
288     # check if used in booking
289     if Host.objects.filter(booked=True, config__image=image).exists():
290         return HttpResponse('no')  # 403?
291     image.delete()
292     return HttpResponse('')