From 012580d98f489e599d44a9a2a0bdc892b9455a90 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 28 May 2021 10:21:39 +0200 Subject: Cleanup We must not expose private actor objects to clients Just make 2 GET requests on channel/accounts instead --- .../+page-not-found/page-not-found.component.ts | 3 +- client/src/app/app-routing.module.ts | 28 +++++++------ .../app/shared/form-validators/user-validators.ts | 4 +- .../shared/shared-main/account/actor.service.ts | 37 ----------------- client/src/app/shared/shared-main/account/index.ts | 1 - client/src/app/shared/shared-main/index.ts | 3 ++ .../router/actor-redirect-guard.service.ts | 46 ++++++++++++++++++++++ client/src/app/shared/shared-main/router/index.ts | 1 + .../app/shared/shared-main/shared-main.module.ts | 10 +++-- 9 files changed, 78 insertions(+), 55 deletions(-) delete mode 100644 client/src/app/shared/shared-main/account/actor.service.ts create mode 100644 client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts create mode 100644 client/src/app/shared/shared-main/router/index.ts (limited to 'client') 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 94b4c8d27..695568898 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 @@ -3,6 +3,7 @@ import { Title } from '@angular/platform-browser' import { Router } from '@angular/router' import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' + @Component({ selector: 'my-page-not-found', templateUrl: './page-not-found.component.html', @@ -10,7 +11,7 @@ import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' }) export class PageNotFoundComponent implements OnInit { status = HttpStatusCode.NOT_FOUND_404 - type: string + type: 'video' | 'other' = 'other' public constructor ( private titleService: Title, diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 4619c4046..444b6f134 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -5,7 +5,8 @@ import { MenuGuards } from '@app/core/routing/menu-guard.service' import { POSSIBLE_LOCALES } from '@shared/core-utils/i18n' import { MetaGuard, PreloadSelectedModulesList } from './core' import { EmptyComponent } from './empty.component' -import { RootComponent } from './root.component' +import { USER_USERNAME_REGEX_CHARACTERS } from './shared/form-validators/user-validators' +import { ActorRedirectGuard } from './shared/shared-main' const routes: Routes = [ { @@ -17,7 +18,8 @@ const routes: Routes = [ }, { path: 'home', - loadChildren: () => import('./+home/home.module').then(m => m.HomeModule) + loadChildren: () => import('./+home/home.module').then(m => m.HomeModule), + canActivateChild: [ MetaGuard ] }, { path: 'my-account', @@ -94,18 +96,22 @@ const routes: Routes = [ { matcher: (url): UrlMatchResult => { // Matches /@:actorName - if (url.length === 1 && url[0].path.match(/^@[\w]+$/gm)) { - return { - consumed: url, - posParams: { - actorName: new UrlSegment(url[0].path.substr(1), {}) - } + const regex = new RegExp(`^@(${USER_USERNAME_REGEX_CHARACTERS}+)$`) + if (url.length !== 1) return null + + const matchResult = url[0].path.match(regex) + if (!matchResult) return null + + return { + consumed: url, + posParams: { + actorName: new UrlSegment(matchResult[1], {}) } } - - return null }, - component: RootComponent + pathMatch: 'full', + canActivate: [ ActorRedirectGuard ], + component: EmptyComponent }, { path: '', diff --git a/client/src/app/shared/form-validators/user-validators.ts b/client/src/app/shared/form-validators/user-validators.ts index fee37e95f..976c97b87 100644 --- a/client/src/app/shared/form-validators/user-validators.ts +++ b/client/src/app/shared/form-validators/user-validators.ts @@ -1,12 +1,14 @@ import { Validators } from '@angular/forms' import { BuildFormValidator } from './form-validator.model' +export const USER_USERNAME_REGEX_CHARACTERS = '[a-z0-9][a-z0-9._]' + export const USER_USERNAME_VALIDATOR: BuildFormValidator = { VALIDATORS: [ Validators.required, Validators.minLength(1), Validators.maxLength(50), - Validators.pattern(/^[a-z0-9][a-z0-9._]*$/) + Validators.pattern(new RegExp(`^${USER_USERNAME_REGEX_CHARACTERS}*$`)) ], MESSAGES: { 'required': $localize`Username is required.`, diff --git a/client/src/app/shared/shared-main/account/actor.service.ts b/client/src/app/shared/shared-main/account/actor.service.ts deleted file mode 100644 index 464ed4519..000000000 --- a/client/src/app/shared/shared-main/account/actor.service.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Observable, ReplaySubject } from 'rxjs' -import { catchError, map, tap } from 'rxjs/operators' -import { HttpClient } from '@angular/common/http' -import { Injectable } from '@angular/core' -import { RestExtractor } from '@app/core' -import { Account as ServerAccount, VideoChannel as ServerVideoChannel } from '@shared/models' -import { environment } from '../../../../environments/environment' - -type KeysOfUnion = T extends T ? keyof T: never -type ServerActor = KeysOfUnion - -@Injectable() -export class ActorService { - static BASE_ACTOR_API_URL = environment.apiUrl + '/api/v1/actors/' - - actorLoaded = new ReplaySubject(1) - - constructor ( - private authHttp: HttpClient, - private restExtractor: RestExtractor - ) {} - - getActorType (actorName: string): Observable { - return this.authHttp.get(ActorService.BASE_ACTOR_API_URL + actorName) - .pipe( - map(actorHash => { - if (actorHash[ 'userId' ]) { - return 'Account' - } - - return 'VideoChannel' - }), - tap(actor => this.actorLoaded.next(actor)), - catchError(res => this.restExtractor.handleError(res)) - ) - } -} diff --git a/client/src/app/shared/shared-main/account/index.ts b/client/src/app/shared/shared-main/account/index.ts index c6cdcd574..b80ddb9f5 100644 --- a/client/src/app/shared/shared-main/account/index.ts +++ b/client/src/app/shared/shared-main/account/index.ts @@ -1,4 +1,3 @@ export * from './account.model' export * from './account.service' export * from './actor.model' -export * from './actor.service' diff --git a/client/src/app/shared/shared-main/index.ts b/client/src/app/shared/shared-main/index.ts index a4d813c06..3a7fd4c34 100644 --- a/client/src/app/shared/shared-main/index.ts +++ b/client/src/app/shared/shared-main/index.ts @@ -5,6 +5,9 @@ export * from './date' export * from './feeds' export * from './loaders' export * from './misc' +export * from './peertube-modal' +export * from './plugins' +export * from './router' export * from './users' export * from './video' export * from './video-caption' diff --git a/client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts b/client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts new file mode 100644 index 000000000..49d61f945 --- /dev/null +++ b/client/src/app/shared/shared-main/router/actor-redirect-guard.service.ts @@ -0,0 +1,46 @@ +import { forkJoin, of } from 'rxjs' +import { catchError, map } from 'rxjs/operators' +import { Injectable } from '@angular/core' +import { ActivatedRouteSnapshot, CanActivate, Router } from '@angular/router' +import { AccountService } from '../account' +import { VideoChannelService } from '../video-channel' + +@Injectable() +export class ActorRedirectGuard implements CanActivate { + + constructor ( + private router: Router, + private accountService: AccountService, + private channelService: VideoChannelService + ) {} + + canActivate (route: ActivatedRouteSnapshot) { + const actorName = route.params.actorName + + return forkJoin([ + this.accountService.getAccount(actorName).pipe(this.orUndefined()), + this.channelService.getVideoChannel(actorName).pipe(this.orUndefined()) + ]).pipe( + map(([ account, channel ]) => { + if (!account && !channel) { + this.router.navigate([ '/404' ]) + return false + } + + if (account) { + this.router.navigate([ `/a/${actorName}` ], { skipLocationChange: true }) + } + + if (channel) { + this.router.navigate([ `/c/${actorName}` ], { skipLocationChange: true }) + } + + return true + }) + ) + } + + private orUndefined () { + return catchError(() => of(undefined)) + } +} diff --git a/client/src/app/shared/shared-main/router/index.ts b/client/src/app/shared/shared-main/router/index.ts new file mode 100644 index 000000000..f4000b674 --- /dev/null +++ b/client/src/app/shared/shared-main/router/index.ts @@ -0,0 +1 @@ +export * from './actor-redirect-guard.service' diff --git a/client/src/app/shared/shared-main/shared-main.module.ts b/client/src/app/shared/shared-main/shared-main.module.ts index f06f25ca5..c8dd01429 100644 --- a/client/src/app/shared/shared-main/shared-main.module.ts +++ b/client/src/app/shared/shared-main/shared-main.module.ts @@ -4,7 +4,7 @@ import { CommonModule, DatePipe } from '@angular/common' import { HttpClientModule } from '@angular/common/http' import { NgModule } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' -import { RouterModule } from '@angular/router' +import { ActivatedRouteSnapshot, RouterModule } from '@angular/router' import { NgbButtonsModule, NgbCollapseModule, @@ -17,7 +17,7 @@ import { import { LoadingBarModule } from '@ngx-loading-bar/core' import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client' import { SharedGlobalIconModule } from '../shared-icons' -import { AccountService, ActorService } from './account' +import { AccountService } from './account' import { AutofocusDirective, BytesPipe, @@ -39,6 +39,7 @@ import { UserHistoryService, UserNotificationsComponent, UserNotificationService import { RedundancyService, VideoImportService, VideoOwnershipService, VideoService } from './video' import { VideoCaptionService } from './video-caption' import { VideoChannelService } from './video-channel' +import { ActorRedirectGuard } from './router' @NgModule({ imports: [ @@ -161,7 +162,6 @@ import { VideoChannelService } from './video-channel' AUTH_INTERCEPTOR_PROVIDER, AccountService, - ActorService, UserHistoryService, UserNotificationService, @@ -175,7 +175,9 @@ import { VideoChannelService } from './video-channel' VideoChannelService, - CustomPageService + CustomPageService, + + ActorRedirectGuard ] }) export class SharedMainModule { } -- cgit v1.2.3