aboutsummaryrefslogtreecommitdiffhomepage
path: root/client/src/app/shared/auth
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2016-10-02 15:39:09 +0200
committerChocobozzz <florian.bigard@gmail.com>2016-10-02 15:39:09 +0200
commita6375e69668ea42e19531c6bc68dcd37f3f7cbd7 (patch)
tree03204a408d56311692c3528bedcf95d2455e94f2 /client/src/app/shared/auth
parent052937db8a8d282eccdbdf38d487ed8d85d3c0a7 (diff)
parentc4403b29ad4db097af528a7f04eea07e0ed320d0 (diff)
downloadPeerTube-a6375e69668ea42e19531c6bc68dcd37f3f7cbd7.tar.gz
PeerTube-a6375e69668ea42e19531c6bc68dcd37f3f7cbd7.tar.zst
PeerTube-a6375e69668ea42e19531c6bc68dcd37f3f7cbd7.zip
Merge branch 'master' into webseed-merged
Diffstat (limited to 'client/src/app/shared/auth')
-rw-r--r--client/src/app/shared/auth/auth-http.service.ts19
-rw-r--r--client/src/app/shared/auth/auth-user.model.ts (renamed from client/src/app/shared/auth/user.model.ts)31
-rw-r--r--client/src/app/shared/auth/auth.service.ts94
-rw-r--r--client/src/app/shared/auth/index.ts2
4 files changed, 106 insertions, 40 deletions
diff --git a/client/src/app/shared/auth/auth-http.service.ts b/client/src/app/shared/auth/auth-http.service.ts
index 9c7ef4389..2392898ca 100644
--- a/client/src/app/shared/auth/auth-http.service.ts
+++ b/client/src/app/shared/auth/auth-http.service.ts
@@ -28,7 +28,7 @@ export class AuthHttp extends Http {
28 return super.request(url, options) 28 return super.request(url, options)
29 .catch((err) => { 29 .catch((err) => {
30 if (err.status === 401) { 30 if (err.status === 401) {
31 return this.handleTokenExpired(err, url, options); 31 return this.handleTokenExpired(url, options);
32 } 32 }
33 33
34 return Observable.throw(err); 34 return Observable.throw(err);
@@ -49,26 +49,29 @@ 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 }
65 67
66 private handleTokenExpired(err: Response, url: string | Request, options: RequestOptionsArgs) { 68 private handleTokenExpired(url: string | Request, options: RequestOptionsArgs) {
67 return this.authService.refreshAccessToken().flatMap(() => { 69 return this.authService.refreshAccessToken()
68 this.setAuthorizationHeader(options.headers); 70 .flatMap(() => {
71 this.setAuthorizationHeader(options.headers);
69 72
70 return super.request(url, options); 73 return super.request(url, options);
71 }); 74 });
72 } 75 }
73 76
74 private setAuthorizationHeader(headers: Headers) { 77 private setAuthorizationHeader(headers: Headers) {
diff --git a/client/src/app/shared/auth/user.model.ts b/client/src/app/shared/auth/auth-user.model.ts
index 98852f835..bdd5ea5a9 100644
--- a/client/src/app/shared/auth/user.model.ts
+++ b/client/src/app/shared/auth/auth-user.model.ts
@@ -1,15 +1,28 @@
1export class User { 1import { User } from '../users';
2
3export class AuthUser extends User {
2 private static KEYS = { 4 private static KEYS = {
5 ID: 'id',
6 ROLE: 'role',
3 USERNAME: 'username' 7 USERNAME: 'username'
4 }; 8 };
5 9
10 id: string;
11 role: string;
6 username: string; 12 username: string;
7 tokens: Tokens; 13 tokens: Tokens;
8 14
9 static load() { 15 static load() {
10 const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME); 16 const usernameLocalStorage = localStorage.getItem(this.KEYS.USERNAME);
11 if (usernameLocalStorage) { 17 if (usernameLocalStorage) {
12 return new User(localStorage.getItem(this.KEYS.USERNAME), Tokens.load()); 18 return new AuthUser(
19 {
20 id: localStorage.getItem(this.KEYS.ID),
21 username: localStorage.getItem(this.KEYS.USERNAME),
22 role: localStorage.getItem(this.KEYS.ROLE)
23 },
24 Tokens.load()
25 );
13 } 26 }
14 27
15 return null; 28 return null;
@@ -17,12 +30,14 @@ export class User {
17 30
18 static flush() { 31 static flush() {
19 localStorage.removeItem(this.KEYS.USERNAME); 32 localStorage.removeItem(this.KEYS.USERNAME);
33 localStorage.removeItem(this.KEYS.ID);
34 localStorage.removeItem(this.KEYS.ROLE);
20 Tokens.flush(); 35 Tokens.flush();
21 } 36 }
22 37
23 constructor(username: string, hash_tokens: any) { 38 constructor(userHash: { id: string, username: string, role: string }, hashTokens: any) {
24 this.username = username; 39 super(userHash);
25 this.tokens = new Tokens(hash_tokens); 40 this.tokens = new Tokens(hashTokens);
26 } 41 }
27 42
28 getAccessToken() { 43 getAccessToken() {
@@ -43,12 +58,14 @@ export class User {
43 } 58 }
44 59
45 save() { 60 save() {
46 localStorage.setItem('username', this.username); 61 localStorage.setItem(AuthUser.KEYS.ID, this.id);
62 localStorage.setItem(AuthUser.KEYS.USERNAME, this.username);
63 localStorage.setItem(AuthUser.KEYS.ROLE, this.role);
47 this.tokens.save(); 64 this.tokens.save();
48 } 65 }
49} 66}
50 67
51// Private class used only by User 68// Private class only used by User
52class Tokens { 69class Tokens {
53 private static KEYS = { 70 private static KEYS = {
54 ACCESS_TOKEN: 'access_token', 71 ACCESS_TOKEN: 'access_token',
diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts
index 584298fff..a30c79c86 100644
--- a/client/src/app/shared/auth/auth.service.ts
+++ b/client/src/app/shared/auth/auth.service.ts
@@ -1,32 +1,39 @@
1import { Injectable } from '@angular/core'; 1import { Injectable } from '@angular/core';
2import { Headers, Http, Response, URLSearchParams } from '@angular/http'; 2import { Headers, Http, Response, URLSearchParams } from '@angular/http';
3import { Router } from '@angular/router';
3import { Observable } from 'rxjs/Observable'; 4import { Observable } from 'rxjs/Observable';
4import { Subject } from 'rxjs/Subject'; 5import { Subject } from 'rxjs/Subject';
5 6
6import { AuthStatus } from './auth-status.model'; 7import { AuthStatus } from './auth-status.model';
7import { User } from './user.model'; 8import { AuthUser } from './auth-user.model';
9import { RestExtractor } from '../rest';
8 10
9@Injectable() 11@Injectable()
10export class AuthService { 12export class AuthService {
11 private static BASE_CLIENT_URL = '/api/v1/users/client'; 13 private static BASE_CLIENT_URL = '/api/v1/clients/local';
12 private static BASE_TOKEN_URL = '/api/v1/users/token'; 14 private static BASE_TOKEN_URL = '/api/v1/users/token';
15 private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me';
13 16
14 loginChangedSource: Observable<AuthStatus>; 17 loginChangedSource: Observable<AuthStatus>;
15 18
16 private clientId: string; 19 private clientId: string;
17 private clientSecret: string; 20 private clientSecret: string;
18 private loginChanged: Subject<AuthStatus>; 21 private loginChanged: Subject<AuthStatus>;
19 private user: User = null; 22 private user: AuthUser = null;
20 23
21 constructor(private http: Http) { 24 constructor(
25 private http: Http,
26 private restExtractor: RestExtractor,
27 private router: Router
28 ) {
22 this.loginChanged = new Subject<AuthStatus>(); 29 this.loginChanged = new Subject<AuthStatus>();
23 this.loginChangedSource = this.loginChanged.asObservable(); 30 this.loginChangedSource = this.loginChanged.asObservable();
24 31
25 // Fetch the client_id/client_secret 32 // Fetch the client_id/client_secret
26 // FIXME: save in local storage? 33 // FIXME: save in local storage?
27 this.http.get(AuthService.BASE_CLIENT_URL) 34 this.http.get(AuthService.BASE_CLIENT_URL)
28 .map(res => res.json()) 35 .map(this.restExtractor.extractDataGet)
29 .catch(this.handleError) 36 .catch((res) => this.restExtractor.handleError(res))
30 .subscribe( 37 .subscribe(
31 result => { 38 result => {
32 this.clientId = result.client_id; 39 this.clientId = result.client_id;
@@ -34,12 +41,15 @@ export class AuthService {
34 console.log('Client credentials loaded.'); 41 console.log('Client credentials loaded.');
35 }, 42 },
36 error => { 43 error => {
37 alert(error); 44 alert(
45 `Cannot retrieve OAuth Client credentials: ${error.text}. \n` +
46 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.'
47 );
38 } 48 }
39 ); 49 );
40 50
41 // Return null if there is nothing to load 51 // Return null if there is nothing to load
42 this.user = User.load(); 52 this.user = AuthUser.load();
43 } 53 }
44 54
45 getRefreshToken() { 55 getRefreshToken() {
@@ -64,10 +74,16 @@ export class AuthService {
64 return this.user.getTokenType(); 74 return this.user.getTokenType();
65 } 75 }
66 76
67 getUser(): User { 77 getUser(): AuthUser {
68 return this.user; 78 return this.user;
69 } 79 }
70 80
81 isAdmin() {
82 if (this.user === null) return false;
83
84 return this.user.isAdmin();
85 }
86
71 isLoggedIn() { 87 isLoggedIn() {
72 if (this.getAccessToken()) { 88 if (this.getAccessToken()) {
73 return true; 89 return true;
@@ -94,21 +110,23 @@ export class AuthService {
94 }; 110 };
95 111
96 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) 112 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options)
97 .map(res => res.json()) 113 .map(this.restExtractor.extractDataGet)
98 .map(res => { 114 .map(res => {
99 res.username = username; 115 res.username = username;
100 return res; 116 return res;
101 }) 117 })
118 .flatMap(res => this.fetchUserInformations(res))
102 .map(res => this.handleLogin(res)) 119 .map(res => this.handleLogin(res))
103 .catch(this.handleError); 120 .catch((res) => this.restExtractor.handleError(res));
104 } 121 }
105 122
106 logout() { 123 logout() {
107 // TODO: make an HTTP request to revoke the tokens 124 // TODO: make an HTTP request to revoke the tokens
108 this.user = null; 125 this.user = null;
109 User.flush();
110 126
111 this.setStatus(AuthStatus.LoggedIn); 127 AuthUser.flush();
128
129 this.setStatus(AuthStatus.LoggedOut);
112 } 130 }
113 131
114 refreshAccessToken() { 132 refreshAccessToken() {
@@ -131,36 +149,64 @@ export class AuthService {
131 }; 149 };
132 150
133 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) 151 return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options)
134 .map(res => res.json()) 152 .map(this.restExtractor.extractDataGet)
135 .map(res => this.handleRefreshToken(res)) 153 .map(res => this.handleRefreshToken(res))
136 .catch(this.handleError); 154 .catch((res: Response) => {
155 // The refresh token is invalid?
156 if (res.status === 400 && res.json() && res.json().error === 'invalid_grant') {
157 console.error('Cannot refresh token -> logout...');
158 this.logout();
159 this.router.navigate(['/login']);
160
161 return Observable.throw({
162 json: '',
163 text: 'You need to reconnect.'
164 });
165 }
166
167 return this.restExtractor.handleError(res);
168 });
137 } 169 }
138 170
139 private setStatus(status: AuthStatus) { 171 private fetchUserInformations (obj: any) {
140 this.loginChanged.next(status); 172 // Do not call authHttp here to avoid circular dependencies headaches
173
174 const headers = new Headers();
175 headers.set('Authorization', `Bearer ${obj.access_token}`);
176
177 return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers })
178 .map(res => res.json())
179 .map(res => {
180 obj.id = res.id;
181 obj.role = res.role;
182 return obj;
183 }
184 );
141 } 185 }
142 186
143 private handleLogin (obj: any) { 187 private handleLogin (obj: any) {
188 const id = obj.id;
144 const username = obj.username; 189 const username = obj.username;
145 const hash_tokens = { 190 const role = obj.role;
191 const hashTokens = {
146 access_token: obj.access_token, 192 access_token: obj.access_token,
147 token_type: obj.token_type, 193 token_type: obj.token_type,
148 refresh_token: obj.refresh_token 194 refresh_token: obj.refresh_token
149 }; 195 };
150 196
151 this.user = new User(username, hash_tokens); 197 this.user = new AuthUser({ id, username, role }, hashTokens);
152 this.user.save(); 198 this.user.save();
153 199
154 this.setStatus(AuthStatus.LoggedIn); 200 this.setStatus(AuthStatus.LoggedIn);
155 } 201 }
156 202
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) { 203 private handleRefreshToken (obj: any) {
163 this.user.refreshTokens(obj.access_token, obj.refresh_token); 204 this.user.refreshTokens(obj.access_token, obj.refresh_token);
164 this.user.save(); 205 this.user.save();
165 } 206 }
207
208 private setStatus(status: AuthStatus) {
209 this.loginChanged.next(status);
210 }
211
166} 212}
diff --git a/client/src/app/shared/auth/index.ts b/client/src/app/shared/auth/index.ts
index aafaacbf1..ebd9e14cd 100644
--- a/client/src/app/shared/auth/index.ts
+++ b/client/src/app/shared/auth/index.ts
@@ -1,4 +1,4 @@
1export * from './auth-http.service'; 1export * from './auth-http.service';
2export * from './auth-status.model'; 2export * from './auth-status.model';
3export * from './auth.service'; 3export * from './auth.service';
4export * from './user.model'; 4export * from './auth-user.model';