|
1 | 1 | # stdlib imports
|
2 | 2 | from functools import reduce
|
| 3 | +from pprint import pprint |
3 | 4 | import collections
|
4 | 5 | import datetime
|
5 |
| -import operator |
6 | 6 | import json
|
| 7 | +import operator |
7 | 8 | import time
|
8 | 9 | import urllib.parse
|
9 |
| -from pprint import pprint |
10 | 10 | # django imports
|
11 | 11 | from django.conf import settings
|
12 | 12 | from django.contrib.auth import get_user_model
|
13 | 13 | from django.contrib.auth.decorators import login_required
|
14 | 14 | from django.contrib.auth.mixins import LoginRequiredMixin
|
| 15 | +from django.contrib.auth.mixins import UserPassesTestMixin |
15 | 16 | from django.db import transaction
|
16 | 17 | from django.db.models import Q, Count, Max, Min
|
17 | 18 | from django.forms import HiddenInput
|
|
55 | 56 | from dbdb.core.models import SystemVisit
|
56 | 57 | from dbdb.core.models import SystemRecommendation
|
57 | 58 |
|
| 59 | + |
| 60 | +UserModel = get_user_model() |
| 61 | + |
| 62 | + |
58 | 63 | # constants
|
59 | 64 |
|
60 | 65 | FILTERGROUP_VISIBLE_LENGTH = 3
|
@@ -132,6 +137,15 @@ def get_removal_url(self):
|
132 | 137 | pass
|
133 | 138 |
|
134 | 139 |
|
| 140 | +# helper functions |
| 141 | + |
| 142 | +def staff_check(user): |
| 143 | + return user.is_staff |
| 144 | + |
| 145 | +def super_user_check(user): |
| 146 | + return user.is_superuser |
| 147 | + |
| 148 | + |
135 | 149 | # class based views
|
136 | 150 |
|
137 | 151 | # ==============================================
|
@@ -753,8 +767,8 @@ def post(self, request):
|
753 | 767 | try:
|
754 | 768 | payload = jwt.decode(
|
755 | 769 | token.encode('utf-8'),
|
756 |
| - settings.SECRET_KEY, |
757 |
| - algorithms=['HS256'] |
| 770 | + settings.SECRET_KEY, |
| 771 | + algorithms=['HS256'] |
758 | 772 | )
|
759 | 773 |
|
760 | 774 | iss = payload.get('iss')
|
@@ -796,42 +810,112 @@ def post(self, request):
|
796 | 810 | pass
|
797 | 811 |
|
798 | 812 | # ==============================================
|
799 |
| -# CreateUser |
| 813 | +# CreateUserView |
800 | 814 | # ==============================================
|
801 |
| -class CreateUser(View): |
| 815 | +class CreateUserView(View): |
| 816 | + |
| 817 | + TOKEN_QUERY_NAME = 'token' |
802 | 818 |
|
803 | 819 | template_name = 'registration/create_user.html'
|
804 | 820 |
|
| 821 | + def decode_token(self, request): |
| 822 | + token = request.GET.get(CreateUserView.TOKEN_QUERY_NAME) |
| 823 | + |
| 824 | + if not token: |
| 825 | + return None |
| 826 | + |
| 827 | + try: |
| 828 | + payload = jwt.decode( |
| 829 | + token.encode('utf-8'), |
| 830 | + settings.SECRET_KEY, |
| 831 | + algorithms=['HS256'], |
| 832 | + verify=True |
| 833 | + ) |
| 834 | + pass |
| 835 | + except jwt.exceptions.ExpiredSignatureError: |
| 836 | + payload = False |
| 837 | + except: |
| 838 | + payload = None |
| 839 | + |
| 840 | + return payload |
| 841 | + |
805 | 842 | def get(self, request, *args, **kwargs):
|
806 |
| - context = { |
807 |
| - 'form': CreateUserForm(auto_id='%s'), |
| 843 | + expired_token = False |
| 844 | + initial = { } |
| 845 | + |
| 846 | + reg_info = self.decode_token(request) |
| 847 | + if reg_info == False: |
| 848 | + expired_token = True |
| 849 | + pass |
| 850 | + elif reg_info and 'sub' in reg_info: |
| 851 | + initial['email'] = reg_info['sub'] |
| 852 | + |
| 853 | + form = CreateUserForm(auto_id='%s', initial=initial) |
| 854 | + |
| 855 | + return render(request, self.template_name, { |
| 856 | + 'title': 'User Registration', |
| 857 | + |
| 858 | + 'expired_token': expired_token, |
| 859 | + 'form': form, |
808 | 860 | 'recaptcha_key': getattr(settings, 'NORECAPTCHA_SITE_KEY'),
|
809 |
| - } |
810 |
| - return render(request, context=context, template_name=self.template_name) |
| 861 | + }) |
811 | 862 |
|
812 | 863 | def post(self, request, *args, **kwargs):
|
813 |
| - form = CreateUserForm(request.POST, auto_id='%s') |
814 |
| - User = get_user_model() |
| 864 | + expired_token = False |
| 865 | + initial = { } |
| 866 | + |
| 867 | + # check for a registration info |
| 868 | + reg_info = self.decode_token(request) |
| 869 | + # if the registration expired `False` then return to login page |
| 870 | + if reg_info == False: |
| 871 | + return redirect(settings.LOGIN_URL + '?status=failed') |
| 872 | + pass |
| 873 | + # if the registration included a subject, use as email address |
| 874 | + elif reg_info and 'sub' in reg_info: |
| 875 | + initial['email'] = reg_info['sub'] |
| 876 | + pass |
| 877 | + |
| 878 | + # create form class (it handles enforcing initial email) |
| 879 | + form = CreateUserForm(request.POST, auto_id='%s', initial=initial) |
815 | 880 |
|
816 | 881 | if form.is_valid():
|
817 |
| - User.objects.create_user( |
818 |
| - username=form.cleaned_data['username'], |
819 |
| - email=form.cleaned_data['email'], |
820 |
| - password=form.cleaned_data['password'] |
821 |
| - ) |
822 |
| - return redirect('/login/?status=success') |
| 882 | + with transaction.atomic(): |
| 883 | + # create user with provided info |
| 884 | + user = UserModel.objects.create_user( |
| 885 | + username=form.cleaned_data['username'], |
| 886 | + email=form.cleaned_data['email'], |
| 887 | + password=form.cleaned_data['password'] |
| 888 | + ) |
| 889 | + |
| 890 | + # associate the user with various systems if specified in registration info |
| 891 | + if reg_info and 'systems' in reg_info: |
| 892 | + system_ids = list( map(int, reg_info['systems']) ) |
| 893 | + |
| 894 | + # NOTE: if registration contained no longer valid system IDs, this will error out |
| 895 | + SystemACL.objects.bulk_create([ |
| 896 | + SystemACL( |
| 897 | + system_id=system_id, |
| 898 | + user_id=user.id |
| 899 | + ) |
| 900 | + for system_id in system_ids |
| 901 | + ]) |
| 902 | + pass |
| 903 | + pass |
823 | 904 |
|
824 |
| - return render(request, context={ |
| 905 | + # end successfully with a redirect to login page |
| 906 | + return redirect(settings.LOGIN_URL + '?status=success') |
| 907 | + |
| 908 | + return render(request, self.template_name, { |
825 | 909 | 'form': form,
|
826 | 910 | 'recaptcha_key': getattr(settings, 'NORECAPTCHA_SITE_KEY'),
|
827 |
| - }, template_name=self.template_name) |
| 911 | + }) |
828 | 912 |
|
829 | 913 | pass
|
830 | 914 |
|
831 | 915 | # ==============================================
|
832 | 916 | # DatabasesEditView
|
833 | 917 | # ==============================================
|
834 |
| -class DatabasesEditView(View, LoginRequiredMixin): |
| 918 | +class DatabasesEditView(LoginRequiredMixin, View): |
835 | 919 |
|
836 | 920 | template_name = 'core/databases-edit.html'
|
837 | 921 |
|
@@ -1218,6 +1302,59 @@ def get(self, request):
|
1218 | 1302 |
|
1219 | 1303 | pass
|
1220 | 1304 |
|
| 1305 | +# ============================================== |
| 1306 | +# SetupUserView |
| 1307 | +# ============================================== |
| 1308 | +class SetupUserView(UserPassesTestMixin, View): |
| 1309 | + |
| 1310 | + TOKEN_QUERY_NAME = 'token' |
| 1311 | + |
| 1312 | + template_name = 'registration/setup_user.html' |
| 1313 | + |
| 1314 | + def build_token(self, email, systems): |
| 1315 | + payload = { |
| 1316 | + 'exp': datetime.datetime.utcnow() + datetime.timedelta(days=7), |
| 1317 | + 'iss': 'setup_user', |
| 1318 | + 'sub': email, |
| 1319 | + 'nbf': datetime.datetime.utcnow(), |
| 1320 | + 'systems': list( map(int, systems) ), |
| 1321 | + } |
| 1322 | + |
| 1323 | + s = jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256') |
| 1324 | + s = s.decode('utf-8') |
| 1325 | + |
| 1326 | + return s |
| 1327 | + |
| 1328 | + def get(self, request, *args, **kwargs): |
| 1329 | + if request.GET.get('action') == 'url' and request.GET.get('email') and request.GET.getlist('systems'): |
| 1330 | + email = request.GET.get('email').lower().strip() |
| 1331 | + systems = request.GET.getlist('systems') |
| 1332 | + |
| 1333 | + response = None |
| 1334 | + |
| 1335 | + if UserModel.objects.filter(email=email).exists(): |
| 1336 | + response = { 'error':'Email already exists' } |
| 1337 | + pass |
| 1338 | + else: |
| 1339 | + url = reverse('create_user') + '?' + urllib.parse.urlencode({ SetupUserView.TOKEN_QUERY_NAME:self.build_token(email, systems) }) |
| 1340 | + url = request.build_absolute_uri(url) |
| 1341 | + |
| 1342 | + response = { 'url':url } |
| 1343 | + pass |
| 1344 | + |
| 1345 | + return JsonResponse(response) |
| 1346 | + |
| 1347 | + return render(request, self.template_name, { |
| 1348 | + 'title': 'User Registration Setup', |
| 1349 | + |
| 1350 | + 'systems': System.objects.all(), |
| 1351 | + }) |
| 1352 | + |
| 1353 | + def test_func(self): |
| 1354 | + return super_user_check(self.request.user) |
| 1355 | + |
| 1356 | + pass |
| 1357 | + |
1221 | 1358 | # ==============================================
|
1222 | 1359 | # StatsView
|
1223 | 1360 | # ==============================================
|
@@ -1408,18 +1545,14 @@ def get(self, request, slug):
|
1408 | 1545 | system_version = system.current()
|
1409 | 1546 | system_features = SystemFeature.objects.filter(system=system_version).select_related('feature').order_by('feature__label')
|
1410 | 1547 |
|
1411 |
| - # If they are logged in, check whether they are allowed to edit |
1412 |
| - user_can_edit = False |
1413 |
| - if request.user.is_authenticated: |
1414 |
| - if request.user.is_superuser: |
1415 |
| - user_can_edit = True |
1416 |
| - else: |
1417 |
| - try: |
1418 |
| - SystemACL.objects.get(system=system, user=request.user) |
1419 |
| - user_can_edit = True |
1420 |
| - except SystemACL.DoesNotExist: |
1421 |
| - pass |
1422 |
| - ## IF |
| 1548 | + # if they are logged in, check whether they are allowed to edit |
| 1549 | + if not request.user.is_authenticated: |
| 1550 | + user_can_edit = False |
| 1551 | + elif request.user.is_superuser: |
| 1552 | + user_can_edit = True |
| 1553 | + else: |
| 1554 | + user_can_edit = SystemACL.objects.filter(system=system, user=request.user).exists() |
| 1555 | + pass |
1423 | 1556 |
|
1424 | 1557 | # Compatible Systems
|
1425 | 1558 | compatible = [
|
|
0 commit comments