Client: check token valitidy at startup
authorChocobozzz <florian.bigard@gmail.com>
Sat, 4 Mar 2017 10:45:47 +0000 (11:45 +0100)
committerChocobozzz <florian.bigard@gmail.com>
Sat, 4 Mar 2017 10:45:47 +0000 (11:45 +0100)
17 files changed:
client/src/app/account/account.component.ts
client/src/app/account/account.module.ts
client/src/app/account/account.service.ts [deleted file]
client/src/app/account/index.ts
client/src/app/app.component.ts
client/src/app/core/auth/auth-status.model.ts [new file with mode: 0644]
client/src/app/core/auth/auth-user.model.ts [new file with mode: 0644]
client/src/app/core/auth/auth.service.ts
client/src/app/core/auth/index.ts
client/src/app/core/menu/menu.component.ts
client/src/app/shared/auth/auth-status.model.ts [deleted file]
client/src/app/shared/auth/auth-user.model.ts [deleted file]
client/src/app/shared/auth/index.ts
client/src/app/shared/shared.module.ts
client/src/app/shared/users/index.ts
client/src/app/shared/users/user.service.ts [new file with mode: 0644]
client/src/app/videos/video-list/video-list.component.ts

index 9b6b5fbf421123f282cc1e2e1a8d065d22d346a7..14452a73eccbdeacb406c1ab6eb87d7f667bc40d 100644 (file)
@@ -4,8 +4,7 @@ import { Router } from '@angular/router';
 
 import { NotificationsService } from 'angular2-notifications';
 
-import { AccountService } from './account.service';
-import { FormReactive, USER_PASSWORD } from '../shared';
+import { FormReactive, UserService, USER_PASSWORD } from '../shared';
 
 @Component({
   selector: 'my-account',
@@ -29,7 +28,7 @@ export class AccountComponent extends FormReactive implements OnInit {
     private formBuilder: FormBuilder,
     private router: Router,
     private notificationsService: NotificationsService,
-    private accountService: AccountService
+    private userService: UserService
   ) {
     super();
   }
@@ -58,7 +57,7 @@ export class AccountComponent extends FormReactive implements OnInit {
       return;
     }
 
-    this.accountService.changePassword(newPassword).subscribe(
+    this.userService.changePassword(newPassword).subscribe(
       () => this.notificationsService.success('Success', 'Password updated.'),
 
       err => this.error = err
index 53f6ba58e22ac22078d8e460966cd62011712573..75f2ee6f9b33d8ac7ecf230073d7306e7327fc48 100644 (file)
@@ -19,8 +19,6 @@ import { SharedModule } from '../shared';
     AccountComponent
   ],
 
-  providers: [
-    AccountService
-  ]
+  providers: []
 })
 export class AccountModule { }
diff --git a/client/src/app/account/account.service.ts b/client/src/app/account/account.service.ts
deleted file mode 100644 (file)
index 0466903..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Injectable } from '@angular/core';
-import 'rxjs/add/operator/catch';
-import 'rxjs/add/operator/map';
-
-import { AuthService } from '../core';
-import { AuthHttp, RestExtractor } from '../shared';
-
-@Injectable()
-export class AccountService {
-  private static BASE_USERS_URL = '/api/v1/users/';
-
-  constructor(
-    private authHttp: AuthHttp,
-    private authService: AuthService,
-    private restExtractor: RestExtractor
-  ) {}
-
-  changePassword(newPassword: string) {
-    const url = AccountService.BASE_USERS_URL + this.authService.getUser().id;
-    const body = {
-      password: newPassword
-    };
-
-    return this.authHttp.put(url, body)
-                        .map(this.restExtractor.extractDataBool)
-                        .catch((res) => this.restExtractor.handleError(res));
-  }
-}
index be03713cb7fb37320606717b608d40fe3d0abb29..9265fa10adf369d32eecb28c443bdc5fd59b8f88 100644 (file)
@@ -1,4 +1,3 @@
 export * from './account-routing.module';
 export * from './account.component';
 export * from './account.module';
-export * from './account.service';
index f487e6c482dab245824e8c7efa8f325d1f0ec715..47d2bfdd2e32bc11480b2350230d13e8bceaf927 100644 (file)
@@ -1,14 +1,17 @@
-import { Component, ViewContainerRef } from '@angular/core';
+import { Component, OnInit, ViewContainerRef } from '@angular/core';
 import { Router } from '@angular/router';
 
 import { MetaService } from 'ng2-meta';
+
+import { AuthService } from './core';
+import { UserService } from './shared';
+
 @Component({
   selector: 'my-app',
   templateUrl: './app.component.html',
   styleUrls: [ './app.component.scss' ]
 })
-
-export class AppComponent {
+export class AppComponent implements OnInit {
   notificationOptions = {
     timeOut: 3000,
     lastOnBottom: true,
@@ -25,9 +28,18 @@ export class AppComponent {
   constructor(
     private router: Router,
     private metaService: MetaService,
+    private authService: AuthService,
+    private userService: UserService,
     viewContainerRef: ViewContainerRef
   ) {}
 
+  ngOnInit() {
+    if (this.authService.isLoggedIn()) {
+      // The service will automatically redirect to the login page if the token is not valid anymore
+      this.userService.checkTokenValidity();
+    }
+  }
+
   isInAdmin() {
     return this.router.url.indexOf('/admin/') !== -1;
   }
diff --git a/client/src/app/core/auth/auth-status.model.ts b/client/src/app/core/auth/auth-status.model.ts
new file mode 100644 (file)
index 0000000..f646bd4
--- /dev/null
@@ -0,0 +1,4 @@
+export enum AuthStatus {
+  LoggedIn,
+  LoggedOut
+}
diff --git a/client/src/app/core/auth/auth-user.model.ts b/client/src/app/core/auth/auth-user.model.ts
new file mode 100644 (file)
index 0000000..5d61954
--- /dev/null
@@ -0,0 +1,118 @@
+// Do not use the barrel (dependency loop)
+import { User } from '../../shared/users/user.model';
+
+export class AuthUser extends User {
+  private static KEYS = {
+    ID: 'id',
+    ROLE: 'role',
+    USERNAME: 'username'
+  };
+
+  tokens: Tokens;
+
+  static load() {
+    const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME);
+    if (usernameLocalStorage) {
+      return new AuthUser(
+        {
+          id: parseInt(localStorage.getItem(this.KEYS.ID)),
+          username: localStorage.getItem(this.KEYS.USERNAME),
+          role: localStorage.getItem(this.KEYS.ROLE)
+        },
+        Tokens.load()
+      );
+    }
+
+    return null;
+  }
+
+  static flush() {
+    localStorage.removeItem(this.KEYS.USERNAME);
+    localStorage.removeItem(this.KEYS.ID);
+    localStorage.removeItem(this.KEYS.ROLE);
+    Tokens.flush();
+  }
+
+  constructor(userHash: { id: number, username: string, role: string }, hashTokens: any) {
+    super(userHash);
+    this.tokens = new Tokens(hashTokens);
+  }
+
+  getAccessToken() {
+    return this.tokens.access_token;
+  }
+
+  getRefreshToken() {
+    return this.tokens.refresh_token;
+  }
+
+  getTokenType() {
+    return this.tokens.token_type;
+  }
+
+  refreshTokens(access_token: string, refresh_token: string) {
+    this.tokens.access_token = access_token;
+    this.tokens.refresh_token = refresh_token;
+  }
+
+  save() {
+    localStorage.setItem(AuthUser.KEYS.ID, this.id.toString());
+    localStorage.setItem(AuthUser.KEYS.USERNAME, this.username);
+    localStorage.setItem(AuthUser.KEYS.ROLE, this.role);
+    this.tokens.save();
+  }
+}
+
+// Private class only used by User
+class Tokens {
+  private static KEYS = {
+    ACCESS_TOKEN: 'access_token',
+    REFRESH_TOKEN: 'refresh_token',
+    TOKEN_TYPE: 'token_type',
+  };
+
+  access_token: string;
+  refresh_token: string;
+  token_type: string;
+
+  static load() {
+    const accessTokenLocalStorage = localStorage.getItem(this.KEYS.ACCESS_TOKEN);
+    const refreshTokenLocalStorage = localStorage.getItem(this.KEYS.REFRESH_TOKEN);
+    const tokenTypeLocalStorage = localStorage.getItem(this.KEYS.TOKEN_TYPE);
+
+    if (accessTokenLocalStorage && refreshTokenLocalStorage && tokenTypeLocalStorage) {
+      return new Tokens({
+        access_token: accessTokenLocalStorage,
+        refresh_token: refreshTokenLocalStorage,
+        token_type: tokenTypeLocalStorage
+      });
+    }
+
+    return null;
+  }
+
+  static flush() {
+    localStorage.removeItem(this.KEYS.ACCESS_TOKEN);
+    localStorage.removeItem(this.KEYS.REFRESH_TOKEN);
+    localStorage.removeItem(this.KEYS.TOKEN_TYPE);
+  }
+
+  constructor(hash?: any) {
+    if (hash) {
+      this.access_token = hash.access_token;
+      this.refresh_token = hash.refresh_token;
+
+      if (hash.token_type === 'bearer') {
+        this.token_type = 'Bearer';
+      } else {
+        this.token_type = hash.token_type;
+      }
+    }
+  }
+
+  save() {
+    localStorage.setItem('access_token', this.access_token);
+    localStorage.setItem('refresh_token', this.refresh_token);
+    localStorage.setItem('token_type', this.token_type);
+  }
+}
index a56adbbad913196370524f98dff33a89484db890..2e73281976c2e6626289701f08fe26913373b04d 100644 (file)
@@ -9,9 +9,9 @@ import 'rxjs/add/observable/throw';
 
 import { NotificationsService } from 'angular2-notifications';
 
+import { AuthStatus } from './auth-status.model';
+import { AuthUser } from './auth-user.model';
 // Do not use the barrel (dependency loop)
-import { AuthStatus } from '../../shared/auth/auth-status.model';
-import { AuthUser } from '../../shared/auth/auth-user.model';
 import { RestExtractor } from '../../shared/rest';
 
 @Injectable()
index cf52c9c7ce7552b978e578a666d07ffe41f3794c..67a18cfbbd389e9df52f55f804539590b03c70c0 100644 (file)
@@ -1 +1,3 @@
+export * from './auth-status.model';
+export * from './auth-user.model';
 export * from './auth.service'
index f1bf6966d63a37a79db5f83dfa9f696d46d3c246..5ca60e5e0abcecf06be607f2d4475fe81d60d18e 100644 (file)
@@ -1,8 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { Router } from '@angular/router';
 
-import { AuthService } from '../auth';
-import { AuthStatus } from '../../shared';
+import { AuthService, AuthStatus } from '../auth';
 
 @Component({
   selector: 'my-menu',
diff --git a/client/src/app/shared/auth/auth-status.model.ts b/client/src/app/shared/auth/auth-status.model.ts
deleted file mode 100644 (file)
index f646bd4..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-export enum AuthStatus {
-  LoggedIn,
-  LoggedOut
-}
diff --git a/client/src/app/shared/auth/auth-user.model.ts b/client/src/app/shared/auth/auth-user.model.ts
deleted file mode 100644 (file)
index f560351..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-import { User } from '../users';
-
-export class AuthUser extends User {
-  private static KEYS = {
-    ID: 'id',
-    ROLE: 'role',
-    USERNAME: 'username'
-  };
-
-  tokens: Tokens;
-
-  static load() {
-    const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME);
-    if (usernameLocalStorage) {
-      return new AuthUser(
-        {
-          id: parseInt(localStorage.getItem(this.KEYS.ID)),
-          username: localStorage.getItem(this.KEYS.USERNAME),
-          role: localStorage.getItem(this.KEYS.ROLE)
-        },
-        Tokens.load()
-      );
-    }
-
-    return null;
-  }
-
-  static flush() {
-    localStorage.removeItem(this.KEYS.USERNAME);
-    localStorage.removeItem(this.KEYS.ID);
-    localStorage.removeItem(this.KEYS.ROLE);
-    Tokens.flush();
-  }
-
-  constructor(userHash: { id: number, username: string, role: string }, hashTokens: any) {
-    super(userHash);
-    this.tokens = new Tokens(hashTokens);
-  }
-
-  getAccessToken() {
-    return this.tokens.access_token;
-  }
-
-  getRefreshToken() {
-    return this.tokens.refresh_token;
-  }
-
-  getTokenType() {
-    return this.tokens.token_type;
-  }
-
-  refreshTokens(access_token: string, refresh_token: string) {
-    this.tokens.access_token = access_token;
-    this.tokens.refresh_token = refresh_token;
-  }
-
-  save() {
-    localStorage.setItem(AuthUser.KEYS.ID, this.id.toString());
-    localStorage.setItem(AuthUser.KEYS.USERNAME, this.username);
-    localStorage.setItem(AuthUser.KEYS.ROLE, this.role);
-    this.tokens.save();
-  }
-}
-
-// Private class only used by User
-class Tokens {
-  private static KEYS = {
-    ACCESS_TOKEN: 'access_token',
-    REFRESH_TOKEN: 'refresh_token',
-    TOKEN_TYPE: 'token_type',
-  };
-
-  access_token: string;
-  refresh_token: string;
-  token_type: string;
-
-  static load() {
-    const accessTokenLocalStorage = localStorage.getItem(this.KEYS.ACCESS_TOKEN);
-    const refreshTokenLocalStorage = localStorage.getItem(this.KEYS.REFRESH_TOKEN);
-    const tokenTypeLocalStorage = localStorage.getItem(this.KEYS.TOKEN_TYPE);
-
-    if (accessTokenLocalStorage && refreshTokenLocalStorage && tokenTypeLocalStorage) {
-      return new Tokens({
-        access_token: accessTokenLocalStorage,
-        refresh_token: refreshTokenLocalStorage,
-        token_type: tokenTypeLocalStorage
-      });
-    }
-
-    return null;
-  }
-
-  static flush() {
-    localStorage.removeItem(this.KEYS.ACCESS_TOKEN);
-    localStorage.removeItem(this.KEYS.REFRESH_TOKEN);
-    localStorage.removeItem(this.KEYS.TOKEN_TYPE);
-  }
-
-  constructor(hash?: any) {
-    if (hash) {
-      this.access_token = hash.access_token;
-      this.refresh_token = hash.refresh_token;
-
-      if (hash.token_type === 'bearer') {
-        this.token_type = 'Bearer';
-      } else {
-        this.token_type = hash.token_type;
-      }
-    }
-  }
-
-  save() {
-    localStorage.setItem('access_token', this.access_token);
-    localStorage.setItem('refresh_token', this.refresh_token);
-    localStorage.setItem('token_type', this.token_type);
-  }
-}
index ce0bd8adfc240834d4bb11ed894f96083dce1930..c488aed695936bda84a723a9a62614c37285cd89 100644 (file)
@@ -1,3 +1 @@
 export * from './auth-http.service';
-export * from './auth-status.model';
-export * from './auth-user.model';
index 0f57ef078b18243840f7ebf925da31a2108aeb82..84cc86c64ef3f94f52bba97a389956941cde077b 100644 (file)
@@ -16,6 +16,7 @@ import { Ng2SmartTableModule } from 'ng2-smart-table';
 import { AUTH_HTTP_PROVIDERS } from './auth';
 import { RestExtractor, RestService } from './rest';
 import { SearchComponent, SearchService } from './search';
+import { UserService } from './users';
 import { VideoAbuseService } from './video-abuse';
 
 @NgModule({
@@ -65,7 +66,8 @@ import { VideoAbuseService } from './video-abuse';
     RestExtractor,
     RestService,
     SearchService,
-    VideoAbuseService
+    VideoAbuseService,
+    UserService
   ]
 })
 export class SharedModule { }
index 5a670ce8f9c25e52f39be4335866407fadd8bda2..ff009e89b49e269651399c8a074db677c45ffeb8 100644 (file)
@@ -1 +1,2 @@
 export * from './user.model';
+export * from './user.service';
diff --git a/client/src/app/shared/users/user.service.ts b/client/src/app/shared/users/user.service.ts
new file mode 100644 (file)
index 0000000..4cf100f
--- /dev/null
@@ -0,0 +1,36 @@
+import { Injectable } from '@angular/core';
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/map';
+
+import { AuthService } from '../../core';
+import { AuthHttp } from '../auth';
+import { RestExtractor } from '../rest';
+
+@Injectable()
+export class UserService {
+  private static BASE_USERS_URL = '/api/v1/users/';
+
+  constructor(
+    private authHttp: AuthHttp,
+    private authService: AuthService,
+    private restExtractor: RestExtractor
+  ) {}
+
+  checkTokenValidity() {
+    const url = UserService.BASE_USERS_URL + 'me';
+
+    // AuthHttp will redirect us to the login page if the oken is not valid anymore
+    this.authHttp.get(url).subscribe(() => { ; });
+  }
+
+  changePassword(newPassword: string) {
+    const url = UserService.BASE_USERS_URL + this.authService.getUser().id;
+    const body = {
+      password: newPassword
+    };
+
+    return this.authHttp.put(url, body)
+                        .map(this.restExtractor.extractDataBool)
+                        .catch((res) => this.restExtractor.handleError(res));
+  }
+}
index b3780f8b66ead38076bc5ad13aa9ecf004891ba7..844e14567883b88b65df53770265c207ddf8a01b 100644 (file)
@@ -9,8 +9,8 @@ import {
   Video,
   VideoService
 } from '../shared';
-import { AuthService } from '../../core';
-import { AuthUser, RestPagination, Search, SearchField } from '../../shared';
+import { AuthService, AuthUser } from '../../core';
+import { RestPagination, Search, SearchField } from '../../shared';
 import { SearchService } from '../../shared';
 
 @Component({