From 954605a804da399317ca62afa2fb9244afa11ebf Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 27 Oct 2017 16:55:03 +0200 Subject: Support roles with rights and add moderator role --- client/src/app/core/auth/auth-user.model.ts | 11 ++++-- client/src/app/core/auth/auth.service.ts | 8 +--- client/src/app/core/auth/index.ts | 2 +- client/src/app/core/auth/login-guard.service.ts | 30 -------------- client/src/app/core/core.module.ts | 6 ++- client/src/app/core/menu/menu-admin.component.html | 10 ++--- client/src/app/core/menu/menu-admin.component.ts | 27 ++++++++++++- client/src/app/core/menu/menu.component.html | 4 +- client/src/app/core/menu/menu.component.ts | 46 +++++++++++++++++++++- client/src/app/core/routing/index.ts | 2 + client/src/app/core/routing/login-guard.service.ts | 30 ++++++++++++++ .../app/core/routing/user-right-guard.service.ts | 35 ++++++++++++++++ 12 files changed, 158 insertions(+), 53 deletions(-) delete mode 100644 client/src/app/core/auth/login-guard.service.ts create mode 100644 client/src/app/core/routing/login-guard.service.ts create mode 100644 client/src/app/core/routing/user-right-guard.service.ts (limited to 'client/src/app/core') diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts index 81bff99a0..085b763ec 100644 --- a/client/src/app/core/auth/auth-user.model.ts +++ b/client/src/app/core/auth/auth-user.model.ts @@ -1,6 +1,7 @@ // Do not use the barrel (dependency loop) -import { UserRole } from '../../../../../shared/models/users/user-role.type' +import { hasUserRight, UserRole } from '../../../../../shared/models/users/user-role' import { User, UserConstructorHash } from '../../shared/users/user.model' +import { UserRight } from '../../../../../shared/models/users/user-right.enum' export type TokenOptions = { accessToken: string @@ -81,7 +82,7 @@ export class AuthUser extends User { id: parseInt(localStorage.getItem(this.KEYS.ID), 10), username: localStorage.getItem(this.KEYS.USERNAME), email: localStorage.getItem(this.KEYS.EMAIL), - role: localStorage.getItem(this.KEYS.ROLE) as UserRole, + role: parseInt(localStorage.getItem(this.KEYS.ROLE), 10) as UserRole, displayNSFW: localStorage.getItem(this.KEYS.DISPLAY_NSFW) === 'true' }, Tokens.load() @@ -122,11 +123,15 @@ export class AuthUser extends User { this.tokens.refreshToken = refreshToken } + hasRight(right: UserRight) { + return hasUserRight(this.role, right) + } + save () { localStorage.setItem(AuthUser.KEYS.ID, this.id.toString()) localStorage.setItem(AuthUser.KEYS.USERNAME, this.username) localStorage.setItem(AuthUser.KEYS.EMAIL, this.email) - localStorage.setItem(AuthUser.KEYS.ROLE, this.role) + localStorage.setItem(AuthUser.KEYS.ROLE, this.role.toString()) localStorage.setItem(AuthUser.KEYS.DISPLAY_NSFW, JSON.stringify(this.displayNSFW)) this.tokens.save() } diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 9ac9ba7bb..df6e5135b 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts @@ -21,7 +21,7 @@ import { // Do not use the barrel (dependency loop) import { RestExtractor } from '../../shared/rest' import { UserLogin } from '../../../../../shared/models/users/user-login.model' -import { User, UserConstructorHash } from '../../shared/users/user.model' +import { UserConstructorHash } from '../../shared/users/user.model' interface UserLoginWithUsername extends UserLogin { access_token: string @@ -126,12 +126,6 @@ export class AuthService { return this.user } - isAdmin () { - if (this.user === null) return false - - return this.user.isAdmin() - } - isLoggedIn () { return !!this.getAccessToken() } diff --git a/client/src/app/core/auth/index.ts b/client/src/app/core/auth/index.ts index a81f2c002..bc7bfec0e 100644 --- a/client/src/app/core/auth/index.ts +++ b/client/src/app/core/auth/index.ts @@ -1,4 +1,4 @@ export * from './auth-status.model' export * from './auth-user.model' export * from './auth.service' -export * from './login-guard.service' +export * from '../routing/login-guard.service' diff --git a/client/src/app/core/auth/login-guard.service.ts b/client/src/app/core/auth/login-guard.service.ts deleted file mode 100644 index c09e8fe97..000000000 --- a/client/src/app/core/auth/login-guard.service.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Injectable } from '@angular/core' -import { - ActivatedRouteSnapshot, - CanActivateChild, - RouterStateSnapshot, - CanActivate, - Router -} from '@angular/router' - -import { AuthService } from './auth.service' - -@Injectable() -export class LoginGuard implements CanActivate, CanActivateChild { - - constructor ( - private router: Router, - private auth: AuthService - ) {} - - canActivate (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { - if (this.auth.isLoggedIn() === true) return true - - this.router.navigate([ '/login' ]) - return false - } - - canActivateChild (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { - return this.canActivate(route, state) - } -} diff --git a/client/src/app/core/core.module.ts b/client/src/app/core/core.module.ts index 163a6bbde..90e2cb190 100644 --- a/client/src/app/core/core.module.ts +++ b/client/src/app/core/core.module.ts @@ -7,7 +7,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import { SimpleNotificationsModule } from 'angular2-notifications' import { ModalModule } from 'ngx-bootstrap/modal' -import { AuthService, LoginGuard } from './auth' +import { AuthService } from './auth' +import { LoginGuard, UserRightGuard } from './routing' import { ServerService } from './server' import { ConfirmComponent, ConfirmService } from './confirm' import { MenuComponent, MenuAdminComponent } from './menu' @@ -42,7 +43,8 @@ import { throwIfAlreadyLoaded } from './module-import-guard' AuthService, ConfirmService, ServerService, - LoginGuard + LoginGuard, + UserRightGuard ] }) export class CoreModule { diff --git a/client/src/app/core/menu/menu-admin.component.html b/client/src/app/core/menu/menu-admin.component.html index edacdee6d..c2b2958b4 100644 --- a/client/src/app/core/menu/menu-admin.component.html +++ b/client/src/app/core/menu/menu-admin.component.html @@ -1,26 +1,26 @@
- + List users - + List friends - + Request stats - + Video abuses - + Video blacklist diff --git a/client/src/app/core/menu/menu-admin.component.ts b/client/src/app/core/menu/menu-admin.component.ts index f6cc6554c..074f1dbaf 100644 --- a/client/src/app/core/menu/menu-admin.component.ts +++ b/client/src/app/core/menu/menu-admin.component.ts @@ -1,8 +1,33 @@ import { Component } from '@angular/core' +import { AuthService } from '../auth/auth.service' +import { UserRight } from '../../../../../shared' + @Component({ selector: 'my-menu-admin', templateUrl: './menu-admin.component.html', styleUrls: [ './menu.component.scss' ] }) -export class MenuAdminComponent { } +export class MenuAdminComponent { + constructor (private auth: AuthService) {} + + hasUsersRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_USERS) + } + + hasFriendsRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_PODS) + } + + hasRequestsStatRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_REQUEST_SCHEDULERS) + } + + hasVideoAbusesRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_ABUSES) + } + + hasVideoBlacklistRight () { + return this.auth.getUser().hasRight(UserRight.MANAGE_VIDEO_BLACKLIST) + } +} diff --git a/client/src/app/core/menu/menu.component.html b/client/src/app/core/menu/menu.component.html index ca341a0fd..2d8aace54 100644 --- a/client/src/app/core/menu/menu.component.html +++ b/client/src/app/core/menu/menu.component.html @@ -39,10 +39,10 @@
-
+
Other
- + Administration diff --git a/client/src/app/core/menu/menu.component.ts b/client/src/app/core/menu/menu.component.ts index 8f15d8838..c66a5eccc 100644 --- a/client/src/app/core/menu/menu.component.ts +++ b/client/src/app/core/menu/menu.component.ts @@ -3,6 +3,7 @@ import { Router } from '@angular/router' import { AuthService, AuthStatus } from '../auth' import { ServerService } from '../server' +import { UserRight } from '../../../../../shared/models/users/user-right.enum' @Component({ selector: 'my-menu', @@ -11,6 +12,15 @@ import { ServerService } from '../server' }) export class MenuComponent implements OnInit { isLoggedIn: boolean + userHasAdminAccess = false + + private routesPerRight = { + [UserRight.MANAGE_USERS]: '/admin/users', + [UserRight.MANAGE_PODS]: '/admin/friends', + [UserRight.MANAGE_REQUEST_SCHEDULERS]: '/admin/requests/stats', + [UserRight.MANAGE_VIDEO_ABUSES]: '/admin/video-abuses', + [UserRight.MANAGE_VIDEO_BLACKLIST]: '/admin/video-blacklist' + } constructor ( private authService: AuthService, @@ -20,14 +30,17 @@ export class MenuComponent implements OnInit { ngOnInit () { this.isLoggedIn = this.authService.isLoggedIn() + this.computeIsUserHasAdminAccess() this.authService.loginChangedSource.subscribe( status => { if (status === AuthStatus.LoggedIn) { this.isLoggedIn = true + this.computeIsUserHasAdminAccess() console.log('Logged in.') } else if (status === AuthStatus.LoggedOut) { this.isLoggedIn = false + this.computeIsUserHasAdminAccess() console.log('Logged out.') } else { console.error('Unknown auth status: ' + status) @@ -40,8 +53,31 @@ export class MenuComponent implements OnInit { return this.serverService.getConfig().signup.allowed } - isUserAdmin () { - return this.authService.isAdmin() + getFirstAdminRightAvailable () { + const user = this.authService.getUser() + if (!user) return undefined + + const adminRights = [ + UserRight.MANAGE_USERS, + UserRight.MANAGE_PODS, + UserRight.MANAGE_REQUEST_SCHEDULERS, + UserRight.MANAGE_VIDEO_ABUSES, + UserRight.MANAGE_VIDEO_BLACKLIST + ] + + for (const adminRight of adminRights) { + if (user.hasRight(adminRight)) { + return adminRight + } + } + + return undefined + } + + getFirstAdminRouteAvailable () { + const right = this.getFirstAdminRightAvailable() + + return this.routesPerRight[right] } logout () { @@ -49,4 +85,10 @@ export class MenuComponent implements OnInit { // Redirect to home page this.router.navigate(['/videos/list']) } + + private computeIsUserHasAdminAccess () { + const right = this.getFirstAdminRightAvailable() + + this.userHasAdminAccess = right !== undefined + } } diff --git a/client/src/app/core/routing/index.ts b/client/src/app/core/routing/index.ts index 17f3ee833..d1b982834 100644 --- a/client/src/app/core/routing/index.ts +++ b/client/src/app/core/routing/index.ts @@ -1 +1,3 @@ +export * from './login-guard.service' +export * from './user-right-guard.service' export * from './preload-selected-modules-list' diff --git a/client/src/app/core/routing/login-guard.service.ts b/client/src/app/core/routing/login-guard.service.ts new file mode 100644 index 000000000..18bc41ca6 --- /dev/null +++ b/client/src/app/core/routing/login-guard.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core' +import { + ActivatedRouteSnapshot, + CanActivateChild, + RouterStateSnapshot, + CanActivate, + Router +} from '@angular/router' + +import { AuthService } from '../auth/auth.service' + +@Injectable() +export class LoginGuard implements CanActivate, CanActivateChild { + + constructor ( + private router: Router, + private auth: AuthService + ) {} + + canActivate (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + if (this.auth.isLoggedIn() === true) return true + + this.router.navigate([ '/login' ]) + return false + } + + canActivateChild (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + return this.canActivate(route, state) + } +} diff --git a/client/src/app/core/routing/user-right-guard.service.ts b/client/src/app/core/routing/user-right-guard.service.ts new file mode 100644 index 000000000..65d029977 --- /dev/null +++ b/client/src/app/core/routing/user-right-guard.service.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core' +import { + ActivatedRouteSnapshot, + CanActivateChild, + RouterStateSnapshot, + CanActivate, + Router +} from '@angular/router' + +import { AuthService } from '../auth' + +@Injectable() +export class UserRightGuard implements CanActivate, CanActivateChild { + + constructor ( + private router: Router, + private auth: AuthService + ) {} + + canActivate (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const user = this.auth.getUser() + if (user) { + const neededUserRight = route.data.userRight + + if (user.hasRight(neededUserRight)) return true + } + + this.router.navigate([ '/login' ]) + return false + } + + canActivateChild (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + return this.canActivate(route, state) + } +} -- cgit v1.2.3