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.contrib import messages
18 from django.contrib.auth import logout, authenticate, login
19 from django.contrib.auth.decorators import login_required
20 from django.contrib.auth.mixins import LoginRequiredMixin
21 from django.contrib.auth.models import User
22 from django.urls import reverse
23 from django.http import HttpResponse
24 from django.utils import timezone
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).order_by("-start"))
202 collab_bookings = list(request.user.collaborators.all().order_by("-start"))
203 context = {"title": "My Bookings", "bookings": bookings, "collab_bookings": collab_bookings}
204 return render(request, template, context=context)
207 def account_configuration_view(request):
208 if not request.user.is_authenticated:
209 return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
210 template = "account/configuration_list.html"
211 configs = list(ConfigBundle.objects.filter(owner=request.user))
212 context = {"title": "Configuration List", "configurations": configs}
213 return render(request, template, context=context)
216 def account_images_view(request):
217 if not request.user.is_authenticated:
218 return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
219 template = "account/image_list.html"
220 my_images = Image.objects.filter(owner=request.user)
221 public_images = Image.objects.filter(public=True)
223 for image in my_images:
224 if Host.objects.filter(booked=True, config__image=image).exists():
225 used_images[image.id] = "true"
229 "public_images": public_images,
230 "used_images": used_images
232 return render(request, template, context=context)
235 def resource_delete_view(request, resource_id=None):
236 if not request.user.is_authenticated:
237 return HttpResponse('no') # 403?
238 grb = get_object_or_404(GenericResourceBundle, pk=resource_id)
239 if not request.user.id == grb.owner.id:
240 return HttpResponse('no') # 403?
241 if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists():
242 return HttpResponse('no') # 403?
244 return HttpResponse('')
247 def configuration_delete_view(request, config_id=None):
248 if not request.user.is_authenticated:
249 return HttpResponse('no') # 403?
250 config = get_object_or_404(ConfigBundle, pk=config_id)
251 if not request.user.id == config.owner.id:
252 return HttpResponse('no') # 403?
253 if Booking.objects.filter(config_bundle=config, end__gt=timezone.now()).exists():
254 return HttpResponse('no')
256 return HttpResponse('')
259 def booking_cancel_view(request, booking_id=None):
260 if not request.user.is_authenticated:
261 return HttpResponse('no') # 403?
262 booking = get_object_or_404(Booking, pk=booking_id)
263 if not request.user.id == booking.owner.id:
264 return HttpResponse('no') # 403?
266 if booking.end < timezone.now(): # booking already over
267 return HttpResponse('')
269 booking.end = timezone.now()
271 return HttpResponse('')
274 def image_delete_view(request, image_id=None):
275 if not request.user.is_authenticated:
276 return HttpResponse('no') # 403?
277 image = get_object_or_404(Image, pk=image_id)
278 if image.public or image.owner.id != request.user.id:
279 return HttpResponse('no') # 403?
280 # check if used in booking
281 if Host.objects.filter(booked=True, config__image=image).exists():
282 return HttpResponse('no') # 403?
284 return HttpResponse('')