diff options
7 files changed, 55 insertions, 22 deletions
diff --git a/client/src/app/+accounts/accounts.component.ts b/client/src/app/+accounts/accounts.component.ts index 1458ea59c..e6a5a5d5e 100644 --- a/client/src/app/+accounts/accounts.component.ts +++ b/client/src/app/+accounts/accounts.component.ts | |||
@@ -50,7 +50,7 @@ export class AccountsComponent implements OnInit, OnDestroy { | |||
50 | switchMap(accountId => this.accountService.getAccount(accountId)), | 50 | switchMap(accountId => this.accountService.getAccount(accountId)), |
51 | tap(account => this.onAccount(account)), | 51 | tap(account => this.onAccount(account)), |
52 | switchMap(account => this.videoChannelService.listAccountVideoChannels(account)), | 52 | switchMap(account => this.videoChannelService.listAccountVideoChannels(account)), |
53 | catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ | 53 | catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'other', [ |
54 | HttpStatusCode.BAD_REQUEST_400, | 54 | HttpStatusCode.BAD_REQUEST_400, |
55 | HttpStatusCode.NOT_FOUND_404 | 55 | HttpStatusCode.NOT_FOUND_404 |
56 | ])) | 56 | ])) |
diff --git a/client/src/app/+page-not-found/page-not-found.component.html b/client/src/app/+page-not-found/page-not-found.component.html index aa57b07e8..efd3cc9f9 100644 --- a/client/src/app/+page-not-found/page-not-found.component.html +++ b/client/src/app/+page-not-found/page-not-found.component.html | |||
@@ -1,23 +1,32 @@ | |||
1 | <div class="root"> | 1 | <div class="root"> |
2 | <div *ngIf="status === 404" class="box"> | 2 | <div *ngIf="status !== 403 && status !== 418" class="box"> |
3 | <strong>{{ status }}.</strong> | 3 | <strong>{{ status }}.</strong> |
4 | <span class="ml-1 text-muted" i18n>That's an error.</span> | 4 | <span class="ml-1 text-muted" i18n>That's an error.</span> |
5 | 5 | ||
6 | <div class="text mt-4" i18n> | 6 | <div class="text mt-4" i18n> |
7 | We couldn't find any ressource tied to the URL {{ pathname }} you were looking for. | 7 | We couldn't find any {{ getRessourceName() }} tied to the URL {{ pathname }} you were looking for. |
8 | </div> | 8 | </div> |
9 | 9 | ||
10 | <div class="text-muted mt-4"> | 10 | <div class="text-muted mt-4"> |
11 | <span i18n="Possible reasons preceding a list of reasons a `Not Found` error page may occur">Possible reasons:</span> | 11 | <span i18n="Possible reasons preceding a list of reasons a `Not Found` error page may occur">Possible reasons:</span> |
12 | 12 | ||
13 | <ul> | 13 | <ul> |
14 | <li i18n>The page may have been moved or deleted</li> | ||
15 | <li i18n>You may have used an outdated or broken link</li> | 14 | <li i18n>You may have used an outdated or broken link</li> |
15 | <li i18n>The {{ getRessourceName() }} may have been moved or deleted</li> | ||
16 | <li i18n>You may have typed the address or URL incorrectly</li> | 16 | <li i18n>You may have typed the address or URL incorrectly</li> |
17 | </ul> | 17 | </ul> |
18 | </div> | 18 | </div> |
19 | </div> | 19 | </div> |
20 | 20 | ||
21 | <div *ngIf="status === 403" class="box"> | ||
22 | <strong>{{ status }}.</strong> | ||
23 | <span class="ml-1 text-muted" i18n>You are not authorized here.</span> | ||
24 | |||
25 | <div class="text mt-4" i18n> | ||
26 | You might need to check your account is allowed by the {{ getRessourceName() }} or instance owner. | ||
27 | </div> | ||
28 | </div> | ||
29 | |||
21 | <div *ngIf="status === 418" class="box"> | 30 | <div *ngIf="status === 418" class="box"> |
22 | <strong>{{ status }}.</strong> | 31 | <strong>{{ status }}.</strong> |
23 | <span class="ml-1 text-muted">I'm a teapot.</span> | 32 | <span class="ml-1 text-muted">I'm a teapot.</span> |
diff --git a/client/src/app/+page-not-found/page-not-found.component.ts b/client/src/app/+page-not-found/page-not-found.component.ts index 81830d415..9302201ea 100644 --- a/client/src/app/+page-not-found/page-not-found.component.ts +++ b/client/src/app/+page-not-found/page-not-found.component.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { Component, OnInit } from '@angular/core' | 1 | import { Component, OnInit } from '@angular/core' |
2 | import { Title } from '@angular/platform-browser' | 2 | import { Title } from '@angular/platform-browser' |
3 | import { Router } from '@angular/router' | ||
3 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | 4 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' |
4 | 5 | ||
5 | @Component({ | 6 | @Component({ |
@@ -9,10 +10,16 @@ import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | |||
9 | }) | 10 | }) |
10 | export class PageNotFoundComponent implements OnInit { | 11 | export class PageNotFoundComponent implements OnInit { |
11 | status = HttpStatusCode.NOT_FOUND_404 | 12 | status = HttpStatusCode.NOT_FOUND_404 |
13 | type: string | ||
12 | 14 | ||
13 | public constructor ( | 15 | public constructor ( |
14 | private titleService: Title | 16 | private titleService: Title, |
15 | ) {} | 17 | private router: Router |
18 | ) { | ||
19 | const state = this.router.getCurrentNavigation()?.extras.state | ||
20 | this.type = state?.type || this.type | ||
21 | this.status = state?.obj.status || this.status | ||
22 | } | ||
16 | 23 | ||
17 | ngOnInit () { | 24 | ngOnInit () { |
18 | if (this.pathname.includes('teapot')) { | 25 | if (this.pathname.includes('teapot')) { |
@@ -25,10 +32,21 @@ export class PageNotFoundComponent implements OnInit { | |||
25 | return window.location.pathname | 32 | return window.location.pathname |
26 | } | 33 | } |
27 | 34 | ||
35 | getRessourceName () { | ||
36 | switch (this.type) { | ||
37 | case 'video': | ||
38 | return $localize`video` | ||
39 | default: | ||
40 | return $localize`ressource` | ||
41 | } | ||
42 | } | ||
43 | |||
28 | getMascotName () { | 44 | getMascotName () { |
29 | switch (this.status) { | 45 | switch (this.status) { |
30 | case HttpStatusCode.I_AM_A_TEAPOT_418: | 46 | case HttpStatusCode.I_AM_A_TEAPOT_418: |
31 | return 'happy' | 47 | return 'happy' |
48 | case HttpStatusCode.FORBIDDEN_403: | ||
49 | return 'arguing' | ||
32 | case HttpStatusCode.NOT_FOUND_404: | 50 | case HttpStatusCode.NOT_FOUND_404: |
33 | default: | 51 | default: |
34 | return 'defeated' | 52 | return 'defeated' |
diff --git a/client/src/app/+video-channels/video-channels.component.ts b/client/src/app/+video-channels/video-channels.component.ts index d2fd265c4..bb601e227 100644 --- a/client/src/app/+video-channels/video-channels.component.ts +++ b/client/src/app/+video-channels/video-channels.component.ts | |||
@@ -38,7 +38,7 @@ export class VideoChannelsComponent implements OnInit, OnDestroy { | |||
38 | map(params => params[ 'videoChannelName' ]), | 38 | map(params => params[ 'videoChannelName' ]), |
39 | distinctUntilChanged(), | 39 | distinctUntilChanged(), |
40 | switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)), | 40 | switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)), |
41 | catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ | 41 | catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'other', [ |
42 | HttpStatusCode.BAD_REQUEST_400, | 42 | HttpStatusCode.BAD_REQUEST_400, |
43 | HttpStatusCode.NOT_FOUND_404 | 43 | HttpStatusCode.NOT_FOUND_404 |
44 | ])) | 44 | ])) |
diff --git a/client/src/app/+videos/+video-watch/video-watch.component.ts b/client/src/app/+videos/+video-watch/video-watch.component.ts index c757a5e93..b698d554f 100644 --- a/client/src/app/+videos/+video-watch/video-watch.component.ts +++ b/client/src/app/+videos/+video-watch/video-watch.component.ts | |||
@@ -404,7 +404,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
404 | this.videoCaptionService.listCaptions(videoId) | 404 | this.videoCaptionService.listCaptions(videoId) |
405 | ]) | 405 | ]) |
406 | .pipe( | 406 | .pipe( |
407 | // If 401, the video is private or blocked so redirect to 404 | 407 | // If 400, 403 or 404, the video is private or blocked so redirect to 404 |
408 | catchError(err => { | 408 | catchError(err => { |
409 | if (err.body.errorCode === ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS && err.body.originUrl) { | 409 | if (err.body.errorCode === ServerErrorCode.DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS && err.body.originUrl) { |
410 | const search = window.location.search | 410 | const search = window.location.search |
@@ -416,9 +416,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
416 | $localize`Redirection` | 416 | $localize`Redirection` |
417 | ).then(res => { | 417 | ).then(res => { |
418 | if (res === false) { | 418 | if (res === false) { |
419 | return this.restExtractor.redirectTo404IfNotFound(err, [ | 419 | return this.restExtractor.redirectTo404IfNotFound(err, 'video', [ |
420 | HttpStatusCode.BAD_REQUEST_400, | 420 | HttpStatusCode.BAD_REQUEST_400, |
421 | HttpStatusCode.UNAUTHORIZED_401, | ||
422 | HttpStatusCode.FORBIDDEN_403, | 421 | HttpStatusCode.FORBIDDEN_403, |
423 | HttpStatusCode.NOT_FOUND_404 | 422 | HttpStatusCode.NOT_FOUND_404 |
424 | ]) | 423 | ]) |
@@ -428,9 +427,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
428 | }) | 427 | }) |
429 | } | 428 | } |
430 | 429 | ||
431 | return this.restExtractor.redirectTo404IfNotFound(err, [ | 430 | return this.restExtractor.redirectTo404IfNotFound(err, 'video', [ |
432 | HttpStatusCode.BAD_REQUEST_400, | 431 | HttpStatusCode.BAD_REQUEST_400, |
433 | HttpStatusCode.UNAUTHORIZED_401, | ||
434 | HttpStatusCode.FORBIDDEN_403, | 432 | HttpStatusCode.FORBIDDEN_403, |
435 | HttpStatusCode.NOT_FOUND_404 | 433 | HttpStatusCode.NOT_FOUND_404 |
436 | ]) | 434 | ]) |
@@ -464,10 +462,9 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
464 | 462 | ||
465 | this.playlistService.getVideoPlaylist(playlistId) | 463 | this.playlistService.getVideoPlaylist(playlistId) |
466 | .pipe( | 464 | .pipe( |
467 | // If 401, the video is private or blocked so redirect to 404 | 465 | // If 400 or 403, the video is private or blocked so redirect to 404 |
468 | catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ | 466 | catchError(err => this.restExtractor.redirectTo404IfNotFound(err, 'video', [ |
469 | HttpStatusCode.BAD_REQUEST_400, | 467 | HttpStatusCode.BAD_REQUEST_400, |
470 | HttpStatusCode.UNAUTHORIZED_401, | ||
471 | HttpStatusCode.FORBIDDEN_403, | 468 | HttpStatusCode.FORBIDDEN_403, |
472 | HttpStatusCode.NOT_FOUND_404 | 469 | HttpStatusCode.NOT_FOUND_404 |
473 | ])) | 470 | ])) |
diff --git a/client/src/app/core/rest/rest-extractor.service.ts b/client/src/app/core/rest/rest-extractor.service.ts index 84d9ed074..b8a95cca6 100644 --- a/client/src/app/core/rest/rest-extractor.service.ts +++ b/client/src/app/core/rest/rest-extractor.service.ts | |||
@@ -93,10 +93,10 @@ export class RestExtractor { | |||
93 | return observableThrowError(errorObj) | 93 | return observableThrowError(errorObj) |
94 | } | 94 | } |
95 | 95 | ||
96 | redirectTo404IfNotFound (obj: { status: number }, status = [ HttpStatusCode.NOT_FOUND_404 ]) { | 96 | redirectTo404IfNotFound (obj: { status: number }, type: 'video' | 'other', status = [ HttpStatusCode.NOT_FOUND_404 ]) { |
97 | if (obj && obj.status && status.indexOf(obj.status) !== -1) { | 97 | if (obj && obj.status && status.indexOf(obj.status) !== -1) { |
98 | // Do not use redirectService to avoid circular dependencies | 98 | // Do not use redirectService to avoid circular dependencies |
99 | this.router.navigate([ '/404' ], { skipLocationChange: true }) | 99 | this.router.navigate([ '/404' ], { state: { type, obj }, skipLocationChange: true }) |
100 | } | 100 | } |
101 | 101 | ||
102 | return observableThrowError(obj) | 102 | return observableThrowError(obj) |
diff --git a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts index 68a4acdb5..3ddaffbdf 100644 --- a/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts +++ b/client/src/app/shared/shared-main/auth/auth-interceptor.service.ts | |||
@@ -1,15 +1,17 @@ | |||
1 | import { Observable, throwError as observableThrowError } from 'rxjs' | 1 | import { Observable, of, throwError as observableThrowError } from 'rxjs' |
2 | import { catchError, switchMap } from 'rxjs/operators' | 2 | import { catchError, switchMap } from 'rxjs/operators' |
3 | import { HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http' | 3 | import { HTTP_INTERCEPTORS, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpErrorResponse } from '@angular/common/http' |
4 | import { Injectable, Injector } from '@angular/core' | 4 | import { Injectable, Injector } from '@angular/core' |
5 | import { AuthService } from '@app/core/auth/auth.service' | 5 | import { AuthService } from '@app/core/auth/auth.service' |
6 | import { Router } from '@angular/router' | ||
7 | import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' | ||
6 | 8 | ||
7 | @Injectable() | 9 | @Injectable() |
8 | export class AuthInterceptor implements HttpInterceptor { | 10 | export class AuthInterceptor implements HttpInterceptor { |
9 | private authService: AuthService | 11 | private authService: AuthService |
10 | 12 | ||
11 | // https://github.com/angular/angular/issues/18224#issuecomment-316957213 | 13 | // https://github.com/angular/angular/issues/18224#issuecomment-316957213 |
12 | constructor (private injector: Injector) {} | 14 | constructor (private injector: Injector, private router: Router) {} |
13 | 15 | ||
14 | intercept (req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { | 16 | intercept (req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { |
15 | if (this.authService === undefined) { | 17 | if (this.authService === undefined) { |
@@ -22,9 +24,11 @@ export class AuthInterceptor implements HttpInterceptor { | |||
22 | // Catch 401 errors (refresh token expired) | 24 | // Catch 401 errors (refresh token expired) |
23 | return next.handle(authReq) | 25 | return next.handle(authReq) |
24 | .pipe( | 26 | .pipe( |
25 | catchError(err => { | 27 | catchError((err: HttpErrorResponse) => { |
26 | if (err.status === 401 && err.error && err.error.code === 'invalid_token') { | 28 | if (err.status === HttpStatusCode.UNAUTHORIZED_401 && err.error && err.error.code === 'invalid_token') { |
27 | return this.handleTokenExpired(req, next) | 29 | return this.handleTokenExpired(req, next) |
30 | } else if (err.status === HttpStatusCode.UNAUTHORIZED_401) { | ||
31 | return this.handleNotAuthenticated(err) | ||
28 | } | 32 | } |
29 | 33 | ||
30 | return observableThrowError(err) | 34 | return observableThrowError(err) |
@@ -51,6 +55,11 @@ export class AuthInterceptor implements HttpInterceptor { | |||
51 | // Clone the request to add the new header | 55 | // Clone the request to add the new header |
52 | return req.clone({ headers: req.headers.set('Authorization', authHeaderValue) }) | 56 | return req.clone({ headers: req.headers.set('Authorization', authHeaderValue) }) |
53 | } | 57 | } |
58 | |||
59 | private handleNotAuthenticated (err: HttpErrorResponse, path = '/login'): Observable<any> { | ||
60 | this.router.navigateByUrl(path) | ||
61 | return of(err.message) | ||
62 | } | ||
54 | } | 63 | } |
55 | 64 | ||
56 | export const AUTH_INTERCEPTOR_PROVIDER = { | 65 | export const AUTH_INTERCEPTOR_PROVIDER = { |