diff --git a/config/settings/base.py b/config/settings/base.py index d19a3cc..f0997bc 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -320,18 +320,24 @@ ACCOUNT_USERNAME_REQUIRED = False ACCOUNT_USER_MODEL_USERNAME_FIELD = None # https://docs.allauth.org/en/latest/account/configuration.html ACCOUNT_EMAIL_VERIFICATION = "mandatory" +ACCOUNT_LOGIN_METHODS = {"email"} ACCOUNT_LOGOUT_ON_GET = True -LOGOUT_ON_PASSWORD_CHANGE = False +ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = False ACCOUNT_CHANGE_EMAIL = True +# ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True +HEADLESS_SERVE_SPECIFICATION = True + ACCOUNT_EMAIL_CONFIRMATION_HMAC = True ACCOUNT_CONFIRM_EMAIL_ON_GET = True ACCOUNT_MAX_EMAIL_ADDRESSES = 2 + ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = None ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = None # ACCOUNT_RATE_LIMITS = { # "confirm_email": "1/4m", # 1 confirmation email every 4 minutes # } HEADLESS_FRONTEND_URLS = { + "account_signup":"http://localhost:3000/account/signup", "account_confirm_email": "http://127.0.0.1:3000/account/email-confirmation/{key}/", # Key placeholders are automatically populated. You are free to adjust this # to your own needs, e.g. @@ -368,7 +374,7 @@ REST_FRAMEWORK = { 'dj_rest_auth.jwt_auth.JWTCookieAuthentication', ), "DEFAULT_PERMISSION_CLASSES": ( - 'rest_framework.permissions.AllowAny', + # 'rest_framework.permissions.AllowAny', "rest_framework.permissions.IsAuthenticated", ), "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", diff --git a/config/settings/local.py b/config/settings/local.py index a24e7a9..1631a4e 100644 --- a/config/settings/local.py +++ b/config/settings/local.py @@ -45,6 +45,7 @@ CORS_ALLOW_HEADERS = [ "user-agent", "x-csrftoken", "x-requested-with", + "x-session-token", ] diff --git a/config/urls.py b/config/urls.py index 3f501bf..e99fb69 100644 --- a/config/urls.py +++ b/config/urls.py @@ -16,37 +16,6 @@ from rest_framework.views import APIView from rest_framework.response import Response 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 = [ @@ -54,10 +23,9 @@ urlpatterns = [ # Django Admin, use {% url 'admin:index' %} path(settings.ADMIN_URL, admin.site.urls), # User management - path("ex/", SetCookieExampleView.as_view(), name="ex"), # path("users/", include("lms.users.urls", namespace="users")), path("accounts/", include("allauth.urls")), - path("auth/", include("allauth.headless.urls")), + # path("auth/", include("allauth.headless.urls")), # Your stuff: custom urls includes go here # ... # Media files @@ -72,15 +40,13 @@ urlpatterns += [ # path('authw/', include('dj_rest_auth.urls')), # 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 - # path("api/", include("config.api_router")), - # DRF auth token - path("api/auth-token/", obtain_auth_token), + # path("api/auth-token/", obtain_auth_token), path("api/schema/", SpectacularAPIView.as_view(), name="api-schema"), path( "api/docs/", diff --git a/lms/accounts/serializers.py b/lms/accounts/serializers.py index 7d16122..efc13c5 100644 --- a/lms/accounts/serializers.py +++ b/lms/accounts/serializers.py @@ -9,11 +9,14 @@ from django.contrib.auth import get_user_model from rest_framework.exceptions import ValidationError from allauth.account.utils import send_email_confirmation from rest_framework.response import Response +from .validation_error import CustomValidationError + User = get_user_model() class CustomLoginSerializer(LoginSerializer): + email = serializers.EmailField(required=True) password = serializers.CharField(style={'input_type': 'password'}, write_only=True) @@ -22,24 +25,28 @@ class CustomLoginSerializer(LoginSerializer): password = attrs.get('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) + email_address = EmailAddress.objects.filter(email=email).first() 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: - 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() if not user.check_password(password): - raise serializers.ValidationError(_("Incorrect password.")) + raise CustomValidationError(_("Incorrect password.")) 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['user'] = user @@ -66,10 +73,10 @@ class CustomRegisterSerializer(RegisterSerializer): email_address = EmailAddress.objects.filter(email=email).first() if email_address: if email_address.verified: - raise ValidationError({'email': 'This email is already.'}) + CustomValidationError({'email': 'This email is already.'}) else: 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.full_name = self.data.get('full_name', '') @@ -88,7 +95,7 @@ class ChangeEmailSerializer(serializers.Serializer): def validate_email(self, value): 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 def save(self, user): diff --git a/lms/accounts/validation_error.py b/lms/accounts/validation_error.py new file mode 100644 index 0000000..f016ee0 --- /dev/null +++ b/lms/accounts/validation_error.py @@ -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 \ No newline at end of file diff --git a/lms/app/views.py b/lms/app/views.py index c5ffd26..2e594d5 100644 --- a/lms/app/views.py +++ b/lms/app/views.py @@ -15,7 +15,7 @@ class CourseViewSet(ModelViewSet): A ViewSet for viewing and editing Course instances. """ queryset = Course.objects.all() - permission_classes = [IsOwnerOrReadOnly] + permission_classes = [IsAuthenticated, IsOwnerOrReadOnly] serializer_class = CourseSerializer def perform_create(self, serializer):