1 ##############################################################################
2 # Copyright (c) 2016 Max Breitenfeldt and others.
3 # Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
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 ##############################################################################
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
30 from rest_framework.authtoken.models import Token
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
39 @method_decorator(login_required, name='dispatch')
40 class AccountSettingsView(UpdateView):
42 form_class = AccountSettingsForm
43 template_name_suffix = '_update_form'
45 def get_success_url(self):
46 messages.add_message(self.request, messages.INFO,
50 def get_object(self, queryset=None):
51 return self.request.user.userprofile
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})
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())
66 # Step 1. Get a request token from Jira.
68 resp, content = client.request(settings.OAUTH_REQUEST_TOKEN_URL, "POST")
70 messages.add_message(self.request, messages.ERROR,
71 'Error: Connection to Jira failed. Please contact an Administrator')
73 if resp['status'] != '200':
74 messages.add_message(self.request, messages.ERROR,
75 'Error: Connection to Jira failed. Please contact an Administrator')
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
87 class JiraLogoutView(LoginRequiredMixin, RedirectView):
88 def get_redirect_url(self, *args, **kwargs):
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())
102 # Step 2. Request the authorized access token from Jira.
104 resp, content = client.request(settings.OAUTH_ACCESS_TOKEN_URL, "POST")
106 messages.add_message(self.request, messages.ERROR,
107 'Error: Connection to Jira failed. Please contact an Administrator')
109 if resp['status'] != '200':
110 messages.add_message(self.request, messages.ERROR,
111 'Error: Connection to Jira failed. Please contact an Administrator')
114 access_token = dict(urllib.parse.parse_qsl(content.decode()))
116 module_dir = os.path.dirname(__file__) # get current directory
117 with open(module_dir + '/rsa.pem', 'r') as f:
121 'access_token': access_token['oauth_token'],
122 'access_token_secret': access_token['oauth_token_secret'],
123 'consumer_key': settings.OAUTH_CONSUMER_KEY,
127 jira = JIRA(server=settings.JIRA_URL, oauth=oauth_dict)
128 username = jira.current_user()
129 email = jira.user(username).emailAddress
131 # Step 3. Lookup the user or create them if they don't exist.
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()
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'])
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
154 @method_decorator(login_required, name='dispatch')
155 class UserListView(TemplateView):
156 template_name = "account/user_list.html"
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})
165 def account_detail_view(request):
166 template = "account/details.html"
167 return render(request, template)
170 def account_resource_view(request):
172 gathers a users genericResoureBundles and
173 turns them into displayable objects
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")
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"
189 "resources": resource_list,
190 "grb_mapping": mapping,
191 "booking_mapping": booking_mapping,
192 "title": "My Resources"
194 return render(request, template, context=context)
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"))
207 "title": "My Bookings",
208 "bookings": bookings,
209 "collab_bookings": collab_bookings,
210 "expired_bookings": expired_bookings
212 return render(request, template, context=context)
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)
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)
231 for image in my_images:
232 if Host.objects.filter(booked=True, config__image=image).exists():
233 used_images[image.id] = "true"
237 "public_images": public_images,
238 "used_images": used_images
240 return render(request, template, context=context)
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?
252 return HttpResponse('')
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')
264 return HttpResponse('')
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?
274 if booking.end < timezone.now(): # booking already over
275 return HttpResponse('')
277 booking.end = timezone.now()
279 return HttpResponse('')
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?
292 return HttpResponse('')