From 876317a9947e52bd10a6d538db5f54585521dff2 Mon Sep 17 00:00:00 2001 From: Chneemann Date: Tue, 6 Aug 2024 04:38:17 +0200 Subject: [PATCH] securing the video page with auth.guard --- backend/videoflix/auth/views.py | 13 +++++++-- backend/videoflix/videoflix/urls.py | 3 +- frontend/src/app/app.routes.ts | 3 +- frontend/src/app/auth.guard.spec.ts | 17 +++++++++++ frontend/src/app/auth.guard.ts | 29 +++++++++++++++++++ .../home/browse/browse.component.ts | 3 ++ frontend/src/app/services/auth.service.ts | 28 ++++++++++++++---- 7 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 frontend/src/app/auth.guard.spec.ts create mode 100644 frontend/src/app/auth.guard.ts diff --git a/backend/videoflix/auth/views.py b/backend/videoflix/auth/views.py index 2ee01c8..b108a75 100644 --- a/backend/videoflix/auth/views.py +++ b/backend/videoflix/auth/views.py @@ -1,7 +1,8 @@ from rest_framework.views import APIView from rest_framework.response import Response -from rest_framework import status +from rest_framework import status, authentication from rest_framework.authtoken.models import Token +from rest_framework.authtoken.views import ObtainAuthToken from django.contrib.auth import authenticate from .serializer import LoginSerializer from users.serializer import UserSerializer @@ -32,7 +33,7 @@ class LoginView(APIView): def _create_token_response(self, user): token, created = Token.objects.get_or_create(user=user) - return Response({'token': token.key}, status=status.HTTP_200_OK) + return Response({token.key}, status=status.HTTP_200_OK) class RegisterView(APIView): def post(self, request): @@ -87,3 +88,11 @@ class VerifyEmailView(APIView): return Response(status=status.HTTP_200_OK) else: return Response({"error": "Invalid email or token."}, status=status.HTTP_400_BAD_REQUEST) + +class AuthView(ObtainAuthToken): + authentication_classes = [authentication.TokenAuthentication] + + def get(self, request): + if request.user.is_authenticated: + return Response(request.user.id, status=status.HTTP_200_OK) + return Response({"error": "User is not logged in"}, status=status.HTTP_401_UNAUTHORIZED) \ No newline at end of file diff --git a/backend/videoflix/videoflix/urls.py b/backend/videoflix/videoflix/urls.py index 9fe4ad8..e172799 100644 --- a/backend/videoflix/videoflix/urls.py +++ b/backend/videoflix/videoflix/urls.py @@ -17,12 +17,13 @@ Including another URLconf from django.contrib import admin from django.urls import path, include from users import views -from auth.views import LoginView, RegisterView, VerifyEmailView +from auth.views import LoginView, RegisterView, VerifyEmailView, AuthView urlpatterns = [ path('admin/', admin.site.urls), path('users/', views.user_list), path('users//', views.user_detail), + path('auth/', AuthView.as_view()), path('auth/login/', LoginView.as_view()), path('auth/register/', RegisterView.as_view()), path('auth/verify-email/', VerifyEmailView.as_view()), diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 1ed769b..8becc5f 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -3,6 +3,7 @@ import { HomeComponent } from './components/home/home.component'; import { ImprintComponent } from './shared/components/legal-information/imprint/imprint.component'; import { PrivacyPolicyComponent } from './shared/components/legal-information/privacy-policy/privacy-policy.component'; import { BrowseComponent } from './components/home/browse/browse.component'; +import { AuthGuard } from './auth.guard'; export const routes: Routes = [ { path: '', component: HomeComponent }, @@ -12,5 +13,5 @@ export const routes: Routes = [ { path: 'verify-email', component: HomeComponent }, { path: 'imprint', component: ImprintComponent }, { path: 'privacy-policy', component: PrivacyPolicyComponent }, - { path: 'browse', component: BrowseComponent }, + { path: 'browse', component: BrowseComponent, canActivate: [AuthGuard] }, ]; diff --git a/frontend/src/app/auth.guard.spec.ts b/frontend/src/app/auth.guard.spec.ts new file mode 100644 index 0000000..4ae275e --- /dev/null +++ b/frontend/src/app/auth.guard.spec.ts @@ -0,0 +1,17 @@ +import { TestBed } from '@angular/core/testing'; +import { CanActivateFn } from '@angular/router'; + +import { authGuard } from './auth.guard'; + +describe('authGuard', () => { + const executeGuard: CanActivateFn = (...guardParameters) => + TestBed.runInInjectionContext(() => authGuard(...guardParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({}); + }); + + it('should be created', () => { + expect(executeGuard).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/auth.guard.ts b/frontend/src/app/auth.guard.ts new file mode 100644 index 0000000..5acb96d --- /dev/null +++ b/frontend/src/app/auth.guard.ts @@ -0,0 +1,29 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { Observable, of } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; +import { AuthService } from './services/auth.service'; + +@Injectable({ + providedIn: 'root', +}) +export class AuthGuard { + constructor(private authService: AuthService, private router: Router) {} + + canActivate(): Observable | Promise | boolean { + return this.authService.checkAuthUser().pipe( + map((isAuthenticated) => { + if (isAuthenticated) { + return true; + } else { + this.router.navigate(['/']); + return false; + } + }), + catchError(() => { + this.router.navigate(['/']); + return of(false); + }) + ); + } +} diff --git a/frontend/src/app/components/home/browse/browse.component.ts b/frontend/src/app/components/home/browse/browse.component.ts index 8f49671..310a311 100644 --- a/frontend/src/app/components/home/browse/browse.component.ts +++ b/frontend/src/app/components/home/browse/browse.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { HeaderComponent } from '../../../shared/components/header/header.component'; import { HeroBannerComponent } from './hero-banner/hero-banner.component'; import { CategoriesComponent } from './categories/categories.component'; +import { AuthService } from '../../../services/auth.service'; @Component({ selector: 'app-browse', @@ -34,6 +35,8 @@ export class BrowseComponent implements OnInit { }, ]; + constructor(private authService: AuthService) {} + currentMovie: any[] = []; playMovie: string = ''; diff --git a/frontend/src/app/services/auth.service.ts b/frontend/src/app/services/auth.service.ts index 062abb3..8fa6e87 100644 --- a/frontend/src/app/services/auth.service.ts +++ b/frontend/src/app/services/auth.service.ts @@ -1,6 +1,6 @@ -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { lastValueFrom } from 'rxjs'; +import { catchError, lastValueFrom, map, Observable, of } from 'rxjs'; import { environment } from '../environments/environment.development'; @Injectable({ @@ -11,6 +11,12 @@ export class AuthService { constructor(private http: HttpClient) {} + async register(body: any) { + await lastValueFrom( + this.http.post(`${environment.baseUrl}/auth/register/`, body) + ); + } + async login(body: any, storage: boolean) { const data = await lastValueFrom( this.http.post(`${environment.baseUrl}/auth/login/`, body) @@ -30,9 +36,21 @@ export class AuthService { : sessionStorage.setItem('authToken', data.toString()); } - async register(body: any) { - await lastValueFrom( - this.http.post(`${environment.baseUrl}/auth/register/`, body) + checkAuthUser(): Observable { + const headers = this.getAuthHeaders(); + return this.http.get(`${environment.baseUrl}/auth/`, { headers }).pipe( + map((response) => true), + catchError(() => of(false)) ); } + + private getAuthHeaders(): HttpHeaders { + let authToken = localStorage.getItem('authToken'); + if (!authToken) { + authToken = sessionStorage.getItem('authToken'); + } + return new HttpHeaders({ + Authorization: `Token ${authToken}`, // Ensure this matches your backend + }); + } }