220 lines
7.6 KiB
Python
220 lines
7.6 KiB
Python
from django.contrib.auth import get_user_model
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib.auth.views import LoginView
|
|
from django.http import Http404
|
|
from django.shortcuts import render, redirect
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from django.utils.decorators import method_decorator
|
|
from django.views.generic import DetailView, UpdateView
|
|
from django_filters.rest_framework import DjangoFilterBackend
|
|
from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope
|
|
from rest_framework import generics, status, permissions
|
|
from rest_framework.permissions import AllowAny
|
|
from rest_framework.response import Response
|
|
from rest_framework.views import APIView
|
|
|
|
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
|
from apps.users.filters import UserFilter
|
|
from apps.users.forms import OTPAuthenticationForm, ProfileUpdateForm
|
|
from apps.users.models import User, InviteRequest
|
|
from apps.users.serializers import PublicUserSerializer, AccountSerializer, RequestOTPSerializer, RequestOTTSerializer, \
|
|
ChangePasswordSerializer, UserInquirySerializer, SessionSerializer, InviteRequestSerializer
|
|
from utils.throttles import RequestOTPDayRateThrottle, RequestOTPMinRateThrottle, NumberedRequestOTPDayRateThrottle, NumberedRequestOTPMinRateThrottle
|
|
|
|
UserModel = get_user_model()
|
|
|
|
|
|
class BaseUserListView(generics.ListAPIView):
|
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
|
queryset = User.objects.all()
|
|
serializer_class = PublicUserSerializer
|
|
filter_backends = (DjangoFilterBackend,)
|
|
|
|
# Make filterset_class abstract - must be defined in subclasses
|
|
filterset_class = None
|
|
|
|
class UserProfileView(BaseUserListView):
|
|
filterset_class = UserFilter
|
|
required_alternate_scopes = {
|
|
"GET": [["accounts.profile:list"]],
|
|
}
|
|
|
|
class UserListView(BaseUserListView):
|
|
# filterset_class = UserFilter
|
|
required_alternate_scopes = {
|
|
"GET": [["accounts.users:list"]],
|
|
}
|
|
|
|
|
|
class UserPublicRetrieveView(generics.RetrieveAPIView):
|
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
|
queryset = User.objects.all()
|
|
serializer_class = PublicUserSerializer
|
|
lookup_field = 'uuid'
|
|
required_alternate_scopes = {
|
|
"GET": [["accounts.profile:retrieve"]],
|
|
}
|
|
|
|
class UserDetailedRetrieveView(generics.RetrieveAPIView):
|
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
|
queryset = User.objects.all()
|
|
serializer_class = AccountSerializer
|
|
lookup_field = 'uuid'
|
|
required_alternate_scopes = {
|
|
"GET": [["accounts.profile:detailed_retrieve"]],
|
|
}
|
|
|
|
class UserCurrentAvatarUrlView(generics.RetrieveAPIView):
|
|
permission_classes = [AllowAny]
|
|
queryset = User.objects.all()
|
|
lookup_field = 'uuid'
|
|
|
|
def retrieve(self, request, *args, **kwargs):
|
|
user: User = self.get_object()
|
|
if user.avatar:
|
|
return redirect(user.avatar.url)
|
|
else:
|
|
raise Http404()
|
|
|
|
|
|
class AccountView(generics.RetrieveUpdateAPIView):
|
|
serializer_class = AccountSerializer
|
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
|
required_alternate_scopes = {
|
|
"GET": [["accounts.account:retrieve"]],
|
|
"POST": [["accounts.account:update"]],
|
|
"PUT": [["accounts.account:update"]],
|
|
"PATCH": [["accounts.account:update"]],
|
|
}
|
|
|
|
def get_object(self):
|
|
return self.request.user
|
|
|
|
def perform_update(self, serializer):
|
|
serializer.save(last_update=timezone.now())
|
|
|
|
|
|
class RequestOTPView(generics.CreateAPIView):
|
|
permission_classes = []
|
|
serializer_class = RequestOTPSerializer
|
|
required_scopes = []
|
|
throttle_classes = [RequestOTPMinRateThrottle,
|
|
RequestOTPDayRateThrottle,
|
|
NumberedRequestOTPDayRateThrottle,
|
|
NumberedRequestOTPMinRateThrottle]
|
|
|
|
class RequestOTTView(generics.CreateAPIView):
|
|
permission_classes = [IsAuthenticatedOrTokenHasScope]
|
|
required_scopes = ['accounts.account:request_ott']
|
|
serializer_class = RequestOTTSerializer
|
|
|
|
def get_object(self):
|
|
return self.request.user
|
|
|
|
def create(self, request, *args, **kwargs):
|
|
instance: User = self.get_object()
|
|
serializer = self.get_serializer(instance, data=request.data)
|
|
serializer.is_valid(raise_exception=True)
|
|
instance.set_ott()
|
|
instance.save()
|
|
return Response(serializer.data)
|
|
|
|
|
|
class UserInquiryView(generics.CreateAPIView):
|
|
serializer_class = UserInquirySerializer
|
|
permission_classes = [IsAuthenticatedOrTokenHasScope]
|
|
required_scopes = ['accounts.profile:inquiry']
|
|
|
|
|
|
class InviteRequestView(generics.CreateAPIView):
|
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
|
required_scopes = ['accounts.invite:submit']
|
|
required_alternate_scopes ={
|
|
"POST": [["accounts.invite:submit"]],
|
|
}
|
|
serializer_class = InviteRequestSerializer
|
|
queryset = InviteRequest.objects.all()
|
|
|
|
class ChangePasswordView(generics.UpdateAPIView):
|
|
permission_classes = [IsAuthenticatedOrTokenHasScope]
|
|
serializer_class = ChangePasswordSerializer
|
|
required_scopes = ["accounts.account:change_password"]
|
|
model = User
|
|
|
|
def get_object(self, queryset=None):
|
|
obj = self.request.user
|
|
return obj
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
self.object = self.get_object()
|
|
serializer = self.get_serializer(data=request.data)
|
|
|
|
if serializer.is_valid():
|
|
pass_field = serializer.data.get("old_password_field")
|
|
old_password = serializer.data.get("old_password")
|
|
new_password = serializer.data.get("new_password")
|
|
if not self.object.check_auth(pass_field, old_password):
|
|
return Response({"old_password": ["Wrong password/otp."]}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
self.object.set_password(new_password)
|
|
self.object.last_update = timezone.now()
|
|
self.object.save()
|
|
return Response({"state": 'success'}, status=status.HTTP_200_OK)
|
|
|
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
class OTPLoginView(LoginView):
|
|
template_name = 'users/login.html'
|
|
"""
|
|
Display the login form and handle the login action.
|
|
"""
|
|
form_class = OTPAuthenticationForm
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
return super().post(request, *args, **kwargs)
|
|
|
|
|
|
@method_decorator(login_required, name='dispatch')
|
|
class ProfileDetailView(DetailView):
|
|
model = User
|
|
template_name = 'users/profile_detail.html'
|
|
pk_url_kwarg = 'uuid'
|
|
|
|
def get_object(self, queryset=None):
|
|
return self.request.user
|
|
|
|
|
|
@method_decorator(login_required, name='dispatch')
|
|
class ProfileUpdateView(UpdateView):
|
|
model = User
|
|
template_name = 'users/profile_update.html'
|
|
pk_url_kwarg = 'uuid'
|
|
form_class = ProfileUpdateForm
|
|
|
|
def get_success_url(self):
|
|
return reverse('users:profile_update')
|
|
|
|
def get_object(self, queryset=None):
|
|
return self.request.user
|
|
|
|
def form_valid(self, form):
|
|
return super().form_valid(form)
|
|
|
|
|
|
class UserSessionListView(generics.ListAPIView):
|
|
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
|
serializer_class = SessionSerializer
|
|
filter_backends = (DjangoFilterBackend,)
|
|
|
|
max_allowed_session = 0
|
|
|
|
required_alternate_scopes = {
|
|
"GET": [["accounts.account:retrieve"]],
|
|
}
|
|
|
|
def get_queryset(self):
|
|
from apps.gooyal_oauth2.models import AccessToken
|
|
return AccessToken.objects.filter(user=self.request.user).all()
|
|
|