add nick
[laas.git] / 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
14 from django.utils import timezone
15 from django.contrib import messages
16 from django.contrib.auth import logout
17 from django.contrib.auth.decorators import login_required
18 from django.contrib.auth.mixins import LoginRequiredMixin
19 from django.contrib.auth.models import User
20 from django.urls import reverse
21 from django.http import HttpResponse
22 from django.shortcuts import get_object_or_404
23 from django.utils.decorators import method_decorator
24 from django.views.generic import RedirectView, TemplateView, UpdateView
25 from django.shortcuts import render
26 from rest_framework.authtoken.models import Token
27 from mozilla_django_oidc.auth import OIDCAuthenticationBackend
28
29
30 from account.forms import AccountSettingsForm
31 from account.models import UserProfile
32 from booking.models import Booking
33 from resource_inventory.models import ResourceTemplate, Image
34
35
36 @method_decorator(login_required, name='dispatch')
37 class AccountSettingsView(UpdateView):
38     model = UserProfile
39     form_class = AccountSettingsForm
40     template_name_suffix = '_update_form'
41
42     def get_success_url(self):
43         messages.add_message(self.request, messages.INFO,
44                              'Settings saved')
45         return '/'
46
47     def get_object(self, queryset=None):
48         return self.request.user.userprofile
49
50     def get_context_data(self, **kwargs):
51         token, created = Token.objects.get_or_create(user=self.request.user)
52         context = super(AccountSettingsView, self).get_context_data(**kwargs)
53         context.update({'title': "Settings", 'token': token})
54         return context
55
56
57 class MyOIDCAB(OIDCAuthenticationBackend):
58     def filter_users_by_claims(self, claims):
59         """
60         Checks to see if user exists and create user if not
61
62         Linux foundation does not allow users to change their
63         username, so chose to match users based on their username.
64         If this changes we will need to match users based on some
65         other criterea.
66         """
67         username = claims.get(os.environ.get('CLAIMS_ENDPOINT') + 'username')
68
69         if not username:
70             return HttpResponse('No username provided, contact support.')
71
72         try:
73             # For literally no (good) reason user needs to be a queryset
74             user = User.objects.filter(username=username)
75             return user
76         except User.DoesNotExist:
77             return self.UserModel.objects.none()
78
79     def create_user(self, claims):
80         """ This creates a user and user profile"""
81         user = super(MyOIDCAB, self).create_user(claims)
82         user.username = claims.get(os.environ['CLAIMS_ENDPOINT'] + 'username')
83         user.save()
84
85         up = UserProfile()
86         up.user = user
87         up.email_addr = claims.get('email')
88         up.save()
89         return user
90
91     def update_user(self, user, claims):
92         """ If their account has different email, change the email """
93         up = UserProfile.objects.get(user=user)
94         up.email_addr = claims.get('email')
95         up.save()
96         return user
97
98
99 class OIDCLoginView(RedirectView):
100     def get_redirect_url(self, *args, **kwargs):
101         return reverse('oidc_authentication_init')
102
103
104 class LogoutView(LoginRequiredMixin, RedirectView):
105     def get_redirect_url(self, *args, **kwargs):
106         logout(self.request)
107         return '/'
108
109
110 @method_decorator(login_required, name='dispatch')
111 class UserListView(TemplateView):
112     template_name = "account/user_list.html"
113
114     def get_context_data(self, **kwargs):
115         users = UserProfile.objects.filter(public_user=True).select_related('user')
116         context = super(UserListView, self).get_context_data(**kwargs)
117         context.update({'title': "Dashboard Users", 'users': users})
118         return context
119
120
121 def account_detail_view(request):
122     template = "account/details.html"
123     return render(request, template)
124
125
126 def account_resource_view(request):
127     """
128     Display a user's resources.
129
130     gathers a users genericResoureBundles and
131     turns them into displayable objects
132     """
133     if not request.user.is_authenticated:
134         return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
135     template = "account/resource_list.html"
136
137     active_bundles = [book.resource for book in Booking.objects.filter(
138         owner=request.user, end__gte=timezone.now(), resource__template__temporary=False)]
139     active_resources = [bundle.template.id for bundle in active_bundles]
140     resource_list = list(ResourceTemplate.objects.filter(owner=request.user, temporary=False))
141
142     context = {
143         "resources": resource_list,
144         "active_resources": active_resources,
145         "title": "My Resources"
146     }
147     return render(request, template, context=context)
148
149
150 def account_booking_view(request):
151     if not request.user.is_authenticated:
152         return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
153     template = "account/booking_list.html"
154     bookings = list(Booking.objects.filter(owner=request.user, end__gt=timezone.now()).order_by("-start"))
155     my_old_bookings = Booking.objects.filter(owner=request.user, end__lt=timezone.now()).order_by("-start")
156     collab_old_bookings = request.user.collaborators.filter(end__lt=timezone.now()).order_by("-start")
157     expired_bookings = list(my_old_bookings.union(collab_old_bookings))
158     collab_bookings = list(request.user.collaborators.filter(end__gt=timezone.now()).order_by("-start"))
159     context = {
160         "title": "My Bookings",
161         "bookings": bookings,
162         "collab_bookings": collab_bookings,
163         "expired_bookings": expired_bookings
164     }
165     return render(request, template, context=context)
166
167
168 def account_images_view(request):
169     if not request.user.is_authenticated:
170         return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
171     template = "account/image_list.html"
172     my_images = Image.objects.filter(owner=request.user)
173     public_images = Image.objects.filter(public=True)
174     used_images = {}
175     for image in my_images:
176         if image.in_use():
177             used_images[image.id] = "true"
178     context = {
179         "title": "Images",
180         "images": my_images,
181         "public_images": public_images,
182         "used_images": used_images
183     }
184     return render(request, template, context=context)
185
186
187 def template_delete_view(request, resource_id=None):
188     if not request.user.is_authenticated:
189         return HttpResponse(status=403)
190     template = get_object_or_404(ResourceTemplate, pk=resource_id)
191     if not request.user.id == template.owner.id:
192         return HttpResponse(status=403)
193     if Booking.objects.filter(resource__template=template, end__gt=timezone.now()).exists():
194         return HttpResponse(status=403)
195     template.public = False
196     template.temporary = True
197     template.save()
198     return HttpResponse(status=200)
199
200
201 def booking_cancel_view(request, booking_id=None):
202     if not request.user.is_authenticated:
203         return HttpResponse('no')  # 403?
204     booking = get_object_or_404(Booking, pk=booking_id)
205     if not request.user.id == booking.owner.id:
206         return HttpResponse('no')  # 403?
207
208     if booking.end < timezone.now():  # booking already over
209         return HttpResponse('')
210
211     booking.end = timezone.now()
212     booking.save()
213     return HttpResponse('')
214
215
216 def image_delete_view(request, image_id=None):
217     if not request.user.is_authenticated:
218         return HttpResponse('no')  # 403?
219     image = get_object_or_404(Image, pk=image_id)
220     if image.public or image.owner.id != request.user.id:
221         return HttpResponse('no')  # 403?
222     # check if used in booking
223     if image.in_use():
224         return HttpResponse('no')  # 403?
225     image.delete()
226     return HttpResponse('')