This commit is contained in:
Ahmed Nagi 2025-01-18 11:15:01 +00:00
parent 4e1b8b9a02
commit 1bf4e86d4c
6 changed files with 42 additions and 49 deletions

View file

@ -320,18 +320,24 @@ ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_USER_MODEL_USERNAME_FIELD = None ACCOUNT_USER_MODEL_USERNAME_FIELD = None
# https://docs.allauth.org/en/latest/account/configuration.html # https://docs.allauth.org/en/latest/account/configuration.html
ACCOUNT_EMAIL_VERIFICATION = "mandatory" ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_LOGIN_METHODS = {"email"}
ACCOUNT_LOGOUT_ON_GET = True ACCOUNT_LOGOUT_ON_GET = True
LOGOUT_ON_PASSWORD_CHANGE = False ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = False
ACCOUNT_CHANGE_EMAIL = True ACCOUNT_CHANGE_EMAIL = True
# ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True
HEADLESS_SERVE_SPECIFICATION = True
ACCOUNT_EMAIL_CONFIRMATION_HMAC = True ACCOUNT_EMAIL_CONFIRMATION_HMAC = True
ACCOUNT_CONFIRM_EMAIL_ON_GET = True ACCOUNT_CONFIRM_EMAIL_ON_GET = True
ACCOUNT_MAX_EMAIL_ADDRESSES = 2 ACCOUNT_MAX_EMAIL_ADDRESSES = 2
ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = None ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = None
ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = None ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = None
# ACCOUNT_RATE_LIMITS = { # ACCOUNT_RATE_LIMITS = {
# "confirm_email": "1/4m", # 1 confirmation email every 4 minutes # "confirm_email": "1/4m", # 1 confirmation email every 4 minutes
# } # }
HEADLESS_FRONTEND_URLS = { HEADLESS_FRONTEND_URLS = {
"account_signup":"http://localhost:3000/account/signup",
"account_confirm_email": "http://127.0.0.1:3000/account/email-confirmation/{key}/", "account_confirm_email": "http://127.0.0.1:3000/account/email-confirmation/{key}/",
# Key placeholders are automatically populated. You are free to adjust this # Key placeholders are automatically populated. You are free to adjust this
# to your own needs, e.g. # to your own needs, e.g.
@ -368,7 +374,7 @@ REST_FRAMEWORK = {
'dj_rest_auth.jwt_auth.JWTCookieAuthentication', 'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
), ),
"DEFAULT_PERMISSION_CLASSES": ( "DEFAULT_PERMISSION_CLASSES": (
'rest_framework.permissions.AllowAny', # 'rest_framework.permissions.AllowAny',
"rest_framework.permissions.IsAuthenticated", "rest_framework.permissions.IsAuthenticated",
), ),
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",

View file

@ -45,6 +45,7 @@ CORS_ALLOW_HEADERS = [
"user-agent", "user-agent",
"x-csrftoken", "x-csrftoken",
"x-requested-with", "x-requested-with",
"x-session-token",
] ]

View file

@ -16,37 +16,6 @@ from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
class SetCookieExampleView(APIView):
permission_classes = [AllowAny]
def post(self, request, *args, **kwargs):
# الحصول على قيمة 'target' من البيانات المرسلة مع الطلب
target = request.META.get('HTTP_ORIGIN', 'No origin provided') # افتراض قيمة افتراضية
# طباعة 'target' في وحدة التحكم
print(f"Target received: {target}")
# إعداد الاستجابة
response = Response({
"message": "Cookie set successfully!",
"target": target, # إضافة 'target' إلى الاستجابة كإخراج اختباري
})
# إعداد الكوكي بدون أمان (HTTP فقط)
response.set_cookie(
key='my_cookie_name', # اسم الكوكي
value='my_cookie_value', # قيمة الكوكي
max_age=3600, # وقت انتهاء الصلاحية بالثواني
httponly=False, # يمكن الوصول للكوكي من JavaScript
samesite=None, # السماح باستخدام الكوكي عبر المواقع (Cross-Site)
)
return response
urlpatterns = [ urlpatterns = [
@ -54,10 +23,9 @@ urlpatterns = [
# Django Admin, use {% url 'admin:index' %} # Django Admin, use {% url 'admin:index' %}
path(settings.ADMIN_URL, admin.site.urls), path(settings.ADMIN_URL, admin.site.urls),
# User management # User management
path("ex/", SetCookieExampleView.as_view(), name="ex"),
# path("users/", include("lms.users.urls", namespace="users")), # path("users/", include("lms.users.urls", namespace="users")),
path("accounts/", include("allauth.urls")), path("accounts/", include("allauth.urls")),
path("auth/", include("allauth.headless.urls")), # path("auth/", include("allauth.headless.urls")),
# Your stuff: custom urls includes go here # Your stuff: custom urls includes go here
# ... # ...
# Media files # Media files
@ -72,15 +40,13 @@ urlpatterns += [
# path('authw/', include('dj_rest_auth.urls')), # path('authw/', include('dj_rest_auth.urls')),
# path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), # path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
# path('auth/', include('lms.accounts.urls')), path('auth/', include('lms.accounts.urls')),
# path('app/', include('lms.app.urls')), path('app/', include('lms.app.urls')),
# API base url # API base url
# path("api/", include("config.api_router")), # path("api/auth-token/", obtain_auth_token),
# DRF auth token
path("api/auth-token/", obtain_auth_token),
path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"), path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"),
path( path(
"api/docs/", "api/docs/",

View file

@ -9,11 +9,14 @@ from django.contrib.auth import get_user_model
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from allauth.account.utils import send_email_confirmation from allauth.account.utils import send_email_confirmation
from rest_framework.response import Response from rest_framework.response import Response
from .validation_error import CustomValidationError
User = get_user_model() User = get_user_model()
class CustomLoginSerializer(LoginSerializer): class CustomLoginSerializer(LoginSerializer):
email = serializers.EmailField(required=True) email = serializers.EmailField(required=True)
password = serializers.CharField(style={'input_type': 'password'}, write_only=True) password = serializers.CharField(style={'input_type': 'password'}, write_only=True)
@ -22,24 +25,28 @@ class CustomLoginSerializer(LoginSerializer):
password = attrs.get('password') password = attrs.get('password')
if not email or not password: if not email or not password:
raise serializers.ValidationError(_("Please enter both email and password.")) raise CustomValidationError(_("Please enter both email and password."))
# البحث عن المستخدم بالبريد الإلكتروني # البحث عن المستخدم بالبريد الإلكتروني
users = User.objects.filter(email=email) users = User.objects.filter(email=email)
email_address = EmailAddress.objects.filter(email=email).first()
if not users.exists(): if not users.exists():
raise serializers.ValidationError(_("No account found with this email.")) raise CustomValidationError(_("No account found with this email."))
if not email_address.verified:
CustomValidationError(_("Email not verified. Please verify your email first."))
if users.count() > 1: if users.count() > 1:
raise serializers.ValidationError(_("Multiple accounts found with this email. Please contact support.")) raise CustomValidationError(_("Multiple accounts found with this email. Please contact support."))
user = users.first() user = users.first()
if not user.check_password(password): if not user.check_password(password):
raise serializers.ValidationError(_("Incorrect password.")) raise CustomValidationError(_("Incorrect password."))
if not self.is_email_verified(user): if not self.is_email_verified(user):
raise serializers.ValidationError(_("Email not verified. Please verify your email first.")) raise CustomValidationError(_("Email not verified. Please verify your email first."))
# إضافة المستخدم إلى الـ attrs # إضافة المستخدم إلى الـ attrs
attrs['user'] = user attrs['user'] = user
@ -66,10 +73,10 @@ class CustomRegisterSerializer(RegisterSerializer):
email_address = EmailAddress.objects.filter(email=email).first() email_address = EmailAddress.objects.filter(email=email).first()
if email_address: if email_address:
if email_address.verified: if email_address.verified:
raise ValidationError({'email': 'This email is already.'}) CustomValidationError({'email': 'This email is already.'})
else: else:
send_email_confirmation(request, email_address.user) send_email_confirmation(request, email_address.user)
raise ValidationError({'email': 'A confirmation email has been sent. Please confirm your email.'}) CustomValidationError({'email': 'A confirmation email has been sent. Please confirm your email.'})
user = super().save(request) user = super().save(request)
user.full_name = self.data.get('full_name', '') user.full_name = self.data.get('full_name', '')
@ -88,7 +95,7 @@ class ChangeEmailSerializer(serializers.Serializer):
def validate_email(self, value): def validate_email(self, value):
if EmailAddress.objects.filter(email=value).exists() or User.objects.filter(email=value).exists(): if EmailAddress.objects.filter(email=value).exists() or User.objects.filter(email=value).exists():
raise serializers.ValidationError("This email is already in use.") raise CustomValidationError("This email is already in use.")
return value return value
def save(self, user): def save(self, user):

View file

@ -0,0 +1,13 @@
from rest_framework.exceptions import APIException
class CustomValidationError(APIException):
status_code = 400
default_detail = 'A validation error occurred.'
default_code = 'validation_error'
def __init__(self, detail=None, code=None):
if detail is not None:
self.detail = detail
if code is not None:
self.status_code = code

View file

@ -15,7 +15,7 @@ class CourseViewSet(ModelViewSet):
A ViewSet for viewing and editing Course instances. A ViewSet for viewing and editing Course instances.
""" """
queryset = Course.objects.all() queryset = Course.objects.all()
permission_classes = [IsOwnerOrReadOnly] permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
serializer_class = CourseSerializer serializer_class = CourseSerializer
def perform_create(self, serializer): def perform_create(self, serializer):