aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src
diff options
context:
space:
mode:
Diffstat (limited to 'client/src')
-rw-r--r--client/src/app/account/account.component.html27
-rw-r--r--client/src/app/account/account.component.ts45
-rw-r--r--client/src/app/account/account.routes.ts5
-rw-r--r--client/src/app/account/account.service.ts19
-rw-r--r--client/src/app/account/index.ts2
-rw-r--r--client/src/app/app.component.html15
-rw-r--r--client/src/app/app.routes.ts2
-rw-r--r--client/src/app/shared/auth/auth-http.service.ts6
-rw-r--r--client/src/app/shared/auth/auth.service.ts37
-rw-r--r--client/src/app/shared/auth/user.model.ts23
10 files changed, 165 insertions, 16 deletions
diff --git a/client/src/app/account/account.component.html b/client/src/app/account/account.component.html
new file mode 100644
index 000000000..ad8f690bd
--- /dev/null
+++ b/client/src/app/account/account.component.html
@@ -0,0 +1,27 @@
1<h3>Account</h3>
2
3<div *ngIf="information" class="alert alert-success">{{ information }}</div>
4<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
5
6<form role="form" (ngSubmit)="changePassword(newPassword.value, newConfirmedPassword.value)" [ngFormModel]="changePasswordForm">
7 <div class="form-group">
8 <label for="new-password">New password</label>
9 <input
10 type="password" class="form-control" name="new-password" id="new-password"
11 ngControl="newPassword" #newPassword="ngForm"
12 >
13 <div [hidden]="newPassword.valid || newPassword.pristine" class="alert alert-warning">
14 The password should have more than 5 characters
15 </div>
16 </div>
17
18 <div class="form-group">
19 <label for="name">Confirm new password</label>
20 <input
21 type="password" class="form-control" name="new-confirmed-password" id="new-confirmed-password"
22 ngControl="newConfirmedPassword" #newConfirmedPassword="ngForm"
23 >
24 </div>
25
26 <input type="submit" value="Change password" class="btn btn-default" [disabled]="!changePasswordForm.valid">
27</form>
diff --git a/client/src/app/account/account.component.ts b/client/src/app/account/account.component.ts
new file mode 100644
index 000000000..5c42103f8
--- /dev/null
+++ b/client/src/app/account/account.component.ts
@@ -0,0 +1,45 @@
1import { Control, ControlGroup, Validators } from '@angular/common';
2import { Component, OnInit } from '@angular/core';
3import { Router } from '@angular/router';
4
5import { AccountService } from './account.service';
6
7@Component({
8 selector: 'my-account',
9 template: require('./account.component.html'),
10 providers: [ AccountService ]
11})
12
13export class AccountComponent implements OnInit {
14 changePasswordForm: ControlGroup;
15 information: string = null;
16 error: string = null;
17
18 constructor(
19 private accountService: AccountService,
20 private router: Router
21 ) {}
22
23 ngOnInit() {
24 this.changePasswordForm = new ControlGroup({
25 newPassword: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])),
26 newConfirmedPassword: new Control('', Validators.compose([ Validators.required, Validators.minLength(6) ])),
27 });
28 }
29
30 changePassword(newPassword: string, newConfirmedPassword: string) {
31 this.information = null;
32 this.error = null;
33
34 if (newPassword !== newConfirmedPassword) {
35 this.error = 'The new password and the confirmed password do not correspond.';
36 return;
37 }
38
39 this.accountService.changePassword(newPassword).subscribe(
40 ok => this.information = 'Password updated.',
41
42 err => this.error = err
43 );
44 }
45}
diff --git a/client/src/app/account/account.routes.ts b/client/src/app/account/account.routes.ts
new file mode 100644
index 000000000..e348c6ebe
--- /dev/null
+++ b/client/src/app/account/account.routes.ts
@@ -0,0 +1,5 @@
1import { AccountComponent } from './account.component';
2
3export const AccountRoutes = [
4 { path: 'account', component: AccountComponent }
5];
diff --git a/client/src/app/account/account.service.ts b/client/src/app/account/account.service.ts
new file mode 100644
index 000000000..19b4e0624
--- /dev/null
+++ b/client/src/app/account/account.service.ts
@@ -0,0 +1,19 @@
1import { Injectable } from '@angular/core';
2
3import { AuthHttp, AuthService } from '../shared';
4
5@Injectable()
6export class AccountService {
7 private static BASE_USERS_URL = '/api/v1/users/';
8
9 constructor(private authHttp: AuthHttp, private authService: AuthService) { }
10
11 changePassword(newPassword: string) {
12 const url = AccountService.BASE_USERS_URL + this.authService.getUser().id;
13 const body = {
14 password: newPassword
15 };
16
17 return this.authHttp.put(url, body);
18 }
19}
diff --git a/client/src/app/account/index.ts b/client/src/app/account/index.ts
new file mode 100644
index 000000000..7445003fd
--- /dev/null
+++ b/client/src/app/account/index.ts
@@ -0,0 +1,2 @@
1export * from './account.component';
2export * from './account.routes';
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html
index f2acffea4..ea4b31421 100644
--- a/client/src/app/app.component.html
+++ b/client/src/app/app.component.html
@@ -18,9 +18,20 @@
18 <menu class="col-md-2 col-sm-3 col-xs-3"> 18 <menu class="col-md-2 col-sm-3 col-xs-3">
19 <div class="panel-block"> 19 <div class="panel-block">
20 <div id="panel-user-login" class="panel-button"> 20 <div id="panel-user-login" class="panel-button">
21 <span *ngIf="!isLoggedIn" >
22 <span class="hidden-xs glyphicon glyphicon-log-in"></span>
23 <a [routerLink]="['/login']">Login</a>
24 </span>
25
26 <span *ngIf="isLoggedIn">
27 <span class="hidden-xs glyphicon glyphicon-log-out"></span>
28 <a *ngIf="isLoggedIn" (click)="logout()">Logout</a>
29 </span>
30 </div>
31
32 <div *ngIf="isLoggedIn" id="panel-user-account" class="panel-button">
21 <span class="hidden-xs glyphicon glyphicon-user"></span> 33 <span class="hidden-xs glyphicon glyphicon-user"></span>
22 <a *ngIf="!isLoggedIn" [routerLink]="['/login']">Login</a> 34 <a [routerLink]="['/account']">My account</a>
23 <a *ngIf="isLoggedIn" (click)="logout()">Logout</a>
24 </div> 35 </div>
25 </div> 36 </div>
26 37
diff --git a/client/src/app/app.routes.ts b/client/src/app/app.routes.ts
index 59ef4ce55..1c414038d 100644
--- a/client/src/app/app.routes.ts
+++ b/client/src/app/app.routes.ts
@@ -1,5 +1,6 @@
1import { RouterConfig } from '@angular/router'; 1import { RouterConfig } from '@angular/router';
2 2
3import { AccountRoutes } from './account';
3import { LoginRoutes } from './login'; 4import { LoginRoutes } from './login';
4import { VideosRoutes } from './videos'; 5import { VideosRoutes } from './videos';
5 6
@@ -10,6 +11,7 @@ export const routes: RouterConfig = [
10 pathMatch: 'full' 11 pathMatch: 'full'
11 }, 12 },
12 13
14 ...AccountRoutes,
13 ...LoginRoutes, 15 ...LoginRoutes,
14 ...VideosRoutes 16 ...VideosRoutes
15]; 17];
diff --git a/client/src/app/shared/auth/auth-http.service.ts b/client/src/app/shared/auth/auth-http.service.ts
index 9c7ef4389..55bb501e6 100644
--- a/client/src/app/shared/auth/auth-http.service.ts
+++ b/client/src/app/shared/auth/auth-http.service.ts
@@ -49,16 +49,18 @@ export class AuthHttp extends Http {
49 return this.request(url, options); 49 return this.request(url, options);
50 } 50 }
51 51
52 post(url: string, options?: RequestOptionsArgs): Observable<Response> { 52 post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
53 if (!options) options = {}; 53 if (!options) options = {};
54 options.method = RequestMethod.Post; 54 options.method = RequestMethod.Post;
55 options.body = body;
55 56
56 return this.request(url, options); 57 return this.request(url, options);
57 } 58 }
58 59
59 put(url: string, options?: RequestOptionsArgs): Observable<Response> { 60 put(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
60 if (!options) options = {}; 61 if (!options) options = {};
61 options.method = RequestMethod.Put; 62 options.method = RequestMethod.Put;
63 options.body = body;
62 64
63 return this.request(url, options); 65 return this.request(url, options);
64 } 66 }
diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts
index 6a5b19ffe..24d1a4fa2 100644
--- a/client/src/app/shared/auth/auth.service.ts
+++ b/client/src/app/shared/auth/auth.service.ts
@@ -10,6 +10,7 @@ import { User } from './user.model';
10export class AuthService { 10export class AuthService {
11 private static BASE_CLIENT_URL = '/api/v1/clients/local'; 11 private static BASE_CLIENT_URL = '/api/v1/clients/local';
12 private static BASE_TOKEN_URL = '/api/v1/users/token'; 12 private static BASE_TOKEN_URL = '/api/v1/users/token';
13 private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me';
13 14
14 loginChangedSource: Observable<AuthStatus>; 15 loginChangedSource: Observable<AuthStatus>;
15 16
@@ -99,6 +100,7 @@ export class AuthService {
99 res.username = username; 100 res.username = username;
100 return res; 101 return res;
101 }) 102 })
103 .flatMap(res => this.fetchUserInformations(res))
102 .map(res => this.handleLogin(res)) 104 .map(res => this.handleLogin(res))
103 .catch(this.handleError); 105 .catch(this.handleError);
104 } 106 }
@@ -136,31 +138,50 @@ export class AuthService {
136 .catch(this.handleError); 138 .catch(this.handleError);
137 } 139 }
138 140
139 private setStatus(status: AuthStatus) { 141 private fetchUserInformations (obj: any) {
140 this.loginChanged.next(status); 142 // Do not call authHttp here to avoid circular dependencies headaches
143
144 const headers = new Headers();
145 headers.set('Authorization', `Bearer ${obj.access_token}`);
146
147 return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers })
148 .map(res => res.json())
149 .map(res => {
150 obj.id = res.id;
151 obj.role = res.role;
152 return obj;
153 }
154 );
155 }
156
157 private handleError (error: Response) {
158 console.error(error);
159 return Observable.throw(error.json() || { error: 'Server error' });
141 } 160 }
142 161
143 private handleLogin (obj: any) { 162 private handleLogin (obj: any) {
163 const id = obj.id;
144 const username = obj.username; 164 const username = obj.username;
165 const role = obj.role;
145 const hash_tokens = { 166 const hash_tokens = {
146 access_token: obj.access_token, 167 access_token: obj.access_token,
147 token_type: obj.token_type, 168 token_type: obj.token_type,
148 refresh_token: obj.refresh_token 169 refresh_token: obj.refresh_token
149 }; 170 };
150 171
151 this.user = new User(username, hash_tokens); 172 this.user = new User(id, username, role, hash_tokens);
152 this.user.save(); 173 this.user.save();
153 174
154 this.setStatus(AuthStatus.LoggedIn); 175 this.setStatus(AuthStatus.LoggedIn);
155 } 176 }
156 177
157 private handleError (error: Response) {
158 console.error(error);
159 return Observable.throw(error.json() || { error: 'Server error' });
160 }
161
162 private handleRefreshToken (obj: any) { 178 private handleRefreshToken (obj: any) {
163 this.user.refreshTokens(obj.access_token, obj.refresh_token); 179 this.user.refreshTokens(obj.access_token, obj.refresh_token);
164 this.user.save(); 180 this.user.save();
165 } 181 }
182
183 private setStatus(status: AuthStatus) {
184 this.loginChanged.next(status);
185 }
186
166} 187}
diff --git a/client/src/app/shared/auth/user.model.ts b/client/src/app/shared/auth/user.model.ts
index 98852f835..e486873ab 100644
--- a/client/src/app/shared/auth/user.model.ts
+++ b/client/src/app/shared/auth/user.model.ts
@@ -1,15 +1,24 @@
1export class User { 1export class User {
2 private static KEYS = { 2 private static KEYS = {
3 ID: 'id',
4 ROLE: 'role',
3 USERNAME: 'username' 5 USERNAME: 'username'
4 }; 6 };
5 7
8 id: string;
9 role: string;
6 username: string; 10 username: string;
7 tokens: Tokens; 11 tokens: Tokens;
8 12
9 static load() { 13 static load() {
10 const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME); 14 const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME);
11 if (usernameLocalStorage) { 15 if (usernameLocalStorage) {
12 return new User(localStorage.getItem(this.KEYS.USERNAME), Tokens.load()); 16 return new User(
17 localStorage.getItem(this.KEYS.ID),
18 localStorage.getItem(this.KEYS.USERNAME),
19 localStorage.getItem(this.KEYS.ROLE),
20 Tokens.load()
21 );
13 } 22 }
14 23
15 return null; 24 return null;
@@ -17,11 +26,15 @@ export class User {
17 26
18 static flush() { 27 static flush() {
19 localStorage.removeItem(this.KEYS.USERNAME); 28 localStorage.removeItem(this.KEYS.USERNAME);
29 localStorage.removeItem(this.KEYS.ID);
30 localStorage.removeItem(this.KEYS.ROLE);
20 Tokens.flush(); 31 Tokens.flush();
21 } 32 }
22 33
23 constructor(username: string, hash_tokens: any) { 34 constructor(id: string, username: string, role: string, hash_tokens: any) {
35 this.id = id;
24 this.username = username; 36 this.username = username;
37 this.role = role;
25 this.tokens = new Tokens(hash_tokens); 38 this.tokens = new Tokens(hash_tokens);
26 } 39 }
27 40
@@ -43,12 +56,14 @@ export class User {
43 } 56 }
44 57
45 save() { 58 save() {
46 localStorage.setItem('username', this.username); 59 localStorage.setItem(User.KEYS.ID, this.id);
60 localStorage.setItem(User.KEYS.USERNAME, this.username);
61 localStorage.setItem(User.KEYS.ROLE, this.role);
47 this.tokens.save(); 62 this.tokens.save();
48 } 63 }
49} 64}
50 65
51// Private class used only by User 66// Private class only used by User
52class Tokens { 67class Tokens {
53 private static KEYS = { 68 private static KEYS = {
54 ACCESS_TOKEN: 'access_token', 69 ACCESS_TOKEN: 'access_token',