diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-09-14 11:57:49 +0200 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-09-14 11:57:49 +0200 |
commit | d592e0a9b2931c7c9cbedb27fb8efc9aaacad9bb (patch) | |
tree | 549b14b842de296efed846a11b3681efe08cfa9e /client/src/app/core/auth | |
parent | 91f6f169b1110eeae6ebf5c387f4204b0d07703c (diff) | |
download | PeerTube-d592e0a9b2931c7c9cbedb27fb8efc9aaacad9bb.tar.gz PeerTube-d592e0a9b2931c7c9cbedb27fb8efc9aaacad9bb.tar.zst PeerTube-d592e0a9b2931c7c9cbedb27fb8efc9aaacad9bb.zip |
Move to HttpClient and PrimeNG data table
Diffstat (limited to 'client/src/app/core/auth')
-rw-r--r-- | client/src/app/core/auth/auth.service.ts | 207 |
1 files changed, 97 insertions, 110 deletions
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index de9e14b2d..522efb23c 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | import { Headers, Http, Response, URLSearchParams } from '@angular/http' | ||
3 | import { Router } from '@angular/router' | 2 | import { Router } from '@angular/router' |
4 | import { Observable } from 'rxjs/Observable' | 3 | import { Observable } from 'rxjs/Observable' |
5 | import { Subject } from 'rxjs/Subject' | 4 | import { Subject } from 'rxjs/Subject' |
5 | import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' | ||
6 | import 'rxjs/add/operator/map' | 6 | import 'rxjs/add/operator/map' |
7 | import 'rxjs/add/operator/mergeMap' | 7 | import 'rxjs/add/operator/mergeMap' |
8 | import 'rxjs/add/observable/throw' | 8 | import 'rxjs/add/observable/throw' |
@@ -11,15 +11,35 @@ import { NotificationsService } from 'angular2-notifications' | |||
11 | 11 | ||
12 | import { AuthStatus } from './auth-status.model' | 12 | import { AuthStatus } from './auth-status.model' |
13 | import { AuthUser } from './auth-user.model' | 13 | import { AuthUser } from './auth-user.model' |
14 | import { OAuthClientLocal, UserRole } from '../../../../../shared' | 14 | import { OAuthClientLocal, UserRole, UserRefreshToken } from '../../../../../shared' |
15 | // Do not use the barrel (dependency loop) | 15 | // Do not use the barrel (dependency loop) |
16 | import { RestExtractor } from '../../shared/rest' | 16 | import { RestExtractor } from '../../shared/rest' |
17 | import { UserLogin } from '../../../../../shared/models/users/user-login.model' | ||
18 | import { User } from '../../shared/users/user.model' | ||
19 | |||
20 | interface UserLoginWithUsername extends UserLogin { | ||
21 | access_token: string | ||
22 | refresh_token: string | ||
23 | token_type: string | ||
24 | username: string | ||
25 | } | ||
26 | |||
27 | interface UserLoginWithUserInformation extends UserLogin { | ||
28 | access_token: string | ||
29 | refresh_token: string | ||
30 | token_type: string | ||
31 | username: string | ||
32 | id: number | ||
33 | role: UserRole | ||
34 | displayNSFW: boolean | ||
35 | email: string | ||
36 | } | ||
17 | 37 | ||
18 | @Injectable() | 38 | @Injectable() |
19 | export class AuthService { | 39 | export class AuthService { |
20 | private static BASE_CLIENT_URL = API_URL + '/api/v1/oauth-clients/local' | 40 | private static BASE_CLIENT_URL = API_URL + '/api/v1/oauth-clients/local' |
21 | private static BASE_TOKEN_URL = API_URL + '/api/v1/users/token' | 41 | private static BASE_TOKEN_URL = API_URL + '/api/v1/users/token' |
22 | private static BASE_USER_INFORMATIONS_URL = API_URL + '/api/v1/users/me' | 42 | private static BASE_USER_INFORMATION_URL = API_URL + '/api/v1/users/me' |
23 | 43 | ||
24 | loginChangedSource: Observable<AuthStatus> | 44 | loginChangedSource: Observable<AuthStatus> |
25 | 45 | ||
@@ -29,7 +49,7 @@ export class AuthService { | |||
29 | private user: AuthUser = null | 49 | private user: AuthUser = null |
30 | 50 | ||
31 | constructor ( | 51 | constructor ( |
32 | private http: Http, | 52 | private http: HttpClient, |
33 | private notificationsService: NotificationsService, | 53 | private notificationsService: NotificationsService, |
34 | private restExtractor: RestExtractor, | 54 | private restExtractor: RestExtractor, |
35 | private router: Router | 55 | private router: Router |
@@ -37,32 +57,33 @@ export class AuthService { | |||
37 | this.loginChanged = new Subject<AuthStatus>() | 57 | this.loginChanged = new Subject<AuthStatus>() |
38 | this.loginChangedSource = this.loginChanged.asObservable() | 58 | this.loginChangedSource = this.loginChanged.asObservable() |
39 | 59 | ||
40 | // Fetch the client_id/client_secret | ||
41 | // FIXME: save in local storage? | ||
42 | this.http.get(AuthService.BASE_CLIENT_URL) | ||
43 | .map(this.restExtractor.extractDataGet) | ||
44 | .catch(res => this.restExtractor.handleError(res)) | ||
45 | .subscribe( | ||
46 | (result: OAuthClientLocal) => { | ||
47 | this.clientId = result.client_id | ||
48 | this.clientSecret = result.client_secret | ||
49 | console.log('Client credentials loaded.') | ||
50 | }, | ||
51 | |||
52 | error => { | ||
53 | let errorMessage = `Cannot retrieve OAuth Client credentials: ${error.text}. \n` | ||
54 | errorMessage += 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.' | ||
55 | |||
56 | // We put a bigger timeout | ||
57 | // This is an important message | ||
58 | this.notificationsService.error('Error', errorMessage, { timeOut: 7000 }) | ||
59 | } | ||
60 | ) | ||
61 | |||
62 | // Return null if there is nothing to load | 60 | // Return null if there is nothing to load |
63 | this.user = AuthUser.load() | 61 | this.user = AuthUser.load() |
64 | } | 62 | } |
65 | 63 | ||
64 | loadClientCredentials () { | ||
65 | // Fetch the client_id/client_secret | ||
66 | // FIXME: save in local storage? | ||
67 | this.http.get<OAuthClientLocal>(AuthService.BASE_CLIENT_URL) | ||
68 | .catch(res => this.restExtractor.handleError(res)) | ||
69 | .subscribe( | ||
70 | res => { | ||
71 | this.clientId = res.client_id | ||
72 | this.clientSecret = res.client_secret | ||
73 | console.log('Client credentials loaded.') | ||
74 | }, | ||
75 | |||
76 | error => { | ||
77 | let errorMessage = `Cannot retrieve OAuth Client credentials: ${error.text}. \n` | ||
78 | errorMessage += 'Ensure you have correctly configured PeerTube (config/ directory), in particular the "webserver" section.' | ||
79 | |||
80 | // We put a bigger timeout | ||
81 | // This is an important message | ||
82 | this.notificationsService.error('Error', errorMessage, { timeOut: 7000 }) | ||
83 | } | ||
84 | ) | ||
85 | } | ||
86 | |||
66 | getRefreshToken () { | 87 | getRefreshToken () { |
67 | if (this.user === null) return null | 88 | if (this.user === null) return null |
68 | 89 | ||
@@ -70,7 +91,11 @@ export class AuthService { | |||
70 | } | 91 | } |
71 | 92 | ||
72 | getRequestHeaderValue () { | 93 | getRequestHeaderValue () { |
73 | return `${this.getTokenType()} ${this.getAccessToken()}` | 94 | const accessToken = this.getAccessToken() |
95 | |||
96 | if (accessToken === null) return null | ||
97 | |||
98 | return `${this.getTokenType()} ${accessToken}` | ||
74 | } | 99 | } |
75 | 100 | ||
76 | getAccessToken () { | 101 | getAccessToken () { |
@@ -96,39 +121,26 @@ export class AuthService { | |||
96 | } | 121 | } |
97 | 122 | ||
98 | isLoggedIn () { | 123 | isLoggedIn () { |
99 | if (this.getAccessToken()) { | 124 | return !!this.getAccessToken() |
100 | return true | ||
101 | } else { | ||
102 | return false | ||
103 | } | ||
104 | } | 125 | } |
105 | 126 | ||
106 | login (username: string, password: string) { | 127 | login (username: string, password: string) { |
107 | let body = new URLSearchParams() | 128 | // Form url encoded |
108 | body.set('client_id', this.clientId) | 129 | const body = new HttpParams().set('client_id', this.clientId) |
109 | body.set('client_secret', this.clientSecret) | 130 | .set('client_secret', this.clientSecret) |
110 | body.set('response_type', 'code') | 131 | .set('response_type', 'code') |
111 | body.set('grant_type', 'password') | 132 | .set('grant_type', 'password') |
112 | body.set('scope', 'upload') | 133 | .set('scope', 'upload') |
113 | body.set('username', username) | 134 | .set('username', username) |
114 | body.set('password', password) | 135 | .set('password', password) |
115 | 136 | ||
116 | let headers = new Headers() | 137 | const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded') |
117 | headers.append('Content-Type', 'application/x-www-form-urlencoded') | 138 | |
118 | 139 | return this.http.post<UserLogin>(AuthService.BASE_TOKEN_URL, body, { headers }) | |
119 | let options = { | 140 | .map(res => Object.assign(res, { username })) |
120 | headers: headers | 141 | .flatMap(res => this.mergeUserInformation(res)) |
121 | } | ||
122 | |||
123 | return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) | ||
124 | .map(this.restExtractor.extractDataGet) | ||
125 | .map(res => { | ||
126 | res.username = username | ||
127 | return res | ||
128 | }) | ||
129 | .flatMap(res => this.mergeUserInformations(res)) | ||
130 | .map(res => this.handleLogin(res)) | 142 | .map(res => this.handleLogin(res)) |
131 | .catch((res) => this.restExtractor.handleError(res)) | 143 | .catch(res => this.restExtractor.handleError(res)) |
132 | } | 144 | } |
133 | 145 | ||
134 | logout () { | 146 | logout () { |
@@ -145,33 +157,26 @@ export class AuthService { | |||
145 | 157 | ||
146 | const refreshToken = this.getRefreshToken() | 158 | const refreshToken = this.getRefreshToken() |
147 | 159 | ||
148 | let body = new URLSearchParams() | 160 | // Form url encoded |
149 | body.set('refresh_token', refreshToken) | 161 | const body = new HttpParams().set('refresh_token', refreshToken) |
150 | body.set('client_id', this.clientId) | 162 | .set('client_id', this.clientId) |
151 | body.set('client_secret', this.clientSecret) | 163 | .set('client_secret', this.clientSecret) |
152 | body.set('response_type', 'code') | 164 | .set('response_type', 'code') |
153 | body.set('grant_type', 'refresh_token') | 165 | .set('grant_type', 'refresh_token') |
154 | |||
155 | let headers = new Headers() | ||
156 | headers.append('Content-Type', 'application/x-www-form-urlencoded') | ||
157 | 166 | ||
158 | let options = { | 167 | const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded') |
159 | headers: headers | ||
160 | } | ||
161 | 168 | ||
162 | return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) | 169 | return this.http.post<UserRefreshToken>(AuthService.BASE_TOKEN_URL, body, { headers }) |
163 | .map(this.restExtractor.extractDataGet) | ||
164 | .map(res => this.handleRefreshToken(res)) | 170 | .map(res => this.handleRefreshToken(res)) |
165 | .catch((res: Response) => { | 171 | .catch(res => { |
166 | // The refresh token is invalid? | 172 | // The refresh token is invalid? |
167 | if (res.status === 400 && res.json() && res.json().error === 'invalid_grant') { | 173 | if (res.status === 400 && res.error === 'invalid_grant') { |
168 | console.error('Cannot refresh token -> logout...') | 174 | console.error('Cannot refresh token -> logout...') |
169 | this.logout() | 175 | this.logout() |
170 | this.router.navigate(['/login']) | 176 | this.router.navigate(['/login']) |
171 | 177 | ||
172 | return Observable.throw({ | 178 | return Observable.throw({ |
173 | json: () => '', | 179 | error: 'You need to reconnect.' |
174 | text: () => 'You need to reconnect.' | ||
175 | }) | 180 | }) |
176 | } | 181 | } |
177 | 182 | ||
@@ -179,7 +184,7 @@ export class AuthService { | |||
179 | }) | 184 | }) |
180 | } | 185 | } |
181 | 186 | ||
182 | refreshUserInformations () { | 187 | refreshUserInformation () { |
183 | const obj = { | 188 | const obj = { |
184 | access_token: this.user.getAccessToken(), | 189 | access_token: this.user.getAccessToken(), |
185 | refresh_token: null, | 190 | refresh_token: null, |
@@ -187,7 +192,7 @@ export class AuthService { | |||
187 | username: this.user.username | 192 | username: this.user.username |
188 | } | 193 | } |
189 | 194 | ||
190 | this.mergeUserInformations (obj) | 195 | this.mergeUserInformation(obj) |
191 | .subscribe( | 196 | .subscribe( |
192 | res => { | 197 | res => { |
193 | this.user.displayNSFW = res.displayNSFW | 198 | this.user.displayNSFW = res.displayNSFW |
@@ -198,42 +203,25 @@ export class AuthService { | |||
198 | ) | 203 | ) |
199 | } | 204 | } |
200 | 205 | ||
201 | private mergeUserInformations (obj: { | 206 | private mergeUserInformation (obj: UserLoginWithUsername): Observable<UserLoginWithUserInformation> { |
202 | access_token: string, | 207 | // User is not loaded yet, set manually auth header |
203 | refresh_token: string, | 208 | const headers = new HttpHeaders().set('Authorization', `${obj.token_type} ${obj.access_token}`) |
204 | token_type: string, | 209 | |
205 | username: string | 210 | return this.http.get<User>(AuthService.BASE_USER_INFORMATION_URL, { headers }) |
206 | }) { | 211 | .map(res => { |
207 | // Do not call authHttp here to avoid circular dependencies headaches | 212 | const newProperties = { |
208 | 213 | id: res.id as number, | |
209 | const headers = new Headers() | 214 | role: res.role as UserRole, |
210 | headers.set('Authorization', `Bearer ${obj.access_token}`) | 215 | displayNSFW: res.displayNSFW as boolean, |
211 | 216 | email: res.email as string | |
212 | return this.http.get(AuthService.BASE_USER_INFORMATIONS_URL, { headers }) | 217 | } |
213 | .map(res => res.json()) | ||
214 | .map(res => { | ||
215 | const newProperties = { | ||
216 | id: res.id as number, | ||
217 | role: res.role as UserRole, | ||
218 | displayNSFW: res.displayNSFW as boolean, | ||
219 | email: res.email as string | ||
220 | } | ||
221 | 218 | ||
222 | return Object.assign(obj, newProperties) | 219 | return Object.assign(obj, newProperties) |
223 | } | 220 | } |
224 | ) | 221 | ) |
225 | } | 222 | } |
226 | 223 | ||
227 | private handleLogin (obj: { | 224 | private handleLogin (obj: UserLoginWithUserInformation) { |
228 | access_token: string, | ||
229 | refresh_token: string, | ||
230 | token_type: string, | ||
231 | id: number, | ||
232 | username: string, | ||
233 | email: string, | ||
234 | role: UserRole, | ||
235 | displayNSFW: boolean | ||
236 | }) { | ||
237 | const id = obj.id | 225 | const id = obj.id |
238 | const username = obj.username | 226 | const username = obj.username |
239 | const role = obj.role | 227 | const role = obj.role |
@@ -251,7 +239,7 @@ export class AuthService { | |||
251 | this.setStatus(AuthStatus.LoggedIn) | 239 | this.setStatus(AuthStatus.LoggedIn) |
252 | } | 240 | } |
253 | 241 | ||
254 | private handleRefreshToken (obj: { access_token: string, refresh_token: string }) { | 242 | private handleRefreshToken (obj: UserRefreshToken) { |
255 | this.user.refreshTokens(obj.access_token, obj.refresh_token) | 243 | this.user.refreshTokens(obj.access_token, obj.refresh_token) |
256 | this.user.save() | 244 | this.user.save() |
257 | } | 245 | } |
@@ -259,5 +247,4 @@ export class AuthService { | |||
259 | private setStatus (status: AuthStatus) { | 247 | private setStatus (status: AuthStatus) { |
260 | this.loginChanged.next(status) | 248 | this.loginChanged.next(status) |
261 | } | 249 | } |
262 | |||
263 | } | 250 | } |