diff options
Diffstat (limited to 'client/src/app/shared')
-rw-r--r-- | client/src/app/shared/auth/auth-http.service.ts | 15 | ||||
-rw-r--r-- | client/src/app/shared/auth/auth.service.ts | 212 | ||||
-rw-r--r-- | client/src/app/shared/auth/index.ts | 1 | ||||
-rw-r--r-- | client/src/app/shared/index.ts | 1 | ||||
-rw-r--r-- | client/src/app/shared/shared.module.ts | 62 |
5 files changed, 76 insertions, 215 deletions
diff --git a/client/src/app/shared/auth/auth-http.service.ts b/client/src/app/shared/auth/auth-http.service.ts index 2392898ca..602726570 100644 --- a/client/src/app/shared/auth/auth-http.service.ts +++ b/client/src/app/shared/auth/auth-http.service.ts | |||
@@ -7,11 +7,12 @@ import { | |||
7 | RequestMethod, | 7 | RequestMethod, |
8 | RequestOptions, | 8 | RequestOptions, |
9 | RequestOptionsArgs, | 9 | RequestOptionsArgs, |
10 | Response | 10 | Response, |
11 | XHRBackend | ||
11 | } from '@angular/http'; | 12 | } from '@angular/http'; |
12 | import { Observable } from 'rxjs/Observable'; | 13 | import { Observable } from 'rxjs/Observable'; |
13 | 14 | ||
14 | import { AuthService } from './auth.service'; | 15 | import { AuthService } from '../../core'; |
15 | 16 | ||
16 | @Injectable() | 17 | @Injectable() |
17 | export class AuthHttp extends Http { | 18 | export class AuthHttp extends Http { |
@@ -78,3 +79,13 @@ export class AuthHttp extends Http { | |||
78 | headers.set('Authorization', this.authService.getRequestHeaderValue()); | 79 | headers.set('Authorization', this.authService.getRequestHeaderValue()); |
79 | } | 80 | } |
80 | } | 81 | } |
82 | |||
83 | export const AUTH_HTTP_PROVIDERS = [ | ||
84 | { | ||
85 | provide: AuthHttp, | ||
86 | useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, authService: AuthService) => { | ||
87 | return new AuthHttp(backend, defaultOptions, authService); | ||
88 | }, | ||
89 | deps: [ XHRBackend, RequestOptions, AuthService ] | ||
90 | }, | ||
91 | ]; | ||
diff --git a/client/src/app/shared/auth/auth.service.ts b/client/src/app/shared/auth/auth.service.ts deleted file mode 100644 index b7e0a44a7..000000000 --- a/client/src/app/shared/auth/auth.service.ts +++ /dev/null | |||
@@ -1,212 +0,0 @@ | |||
1 | import { Injectable } from '@angular/core'; | ||
2 | import { Headers, Http, Response, URLSearchParams } from '@angular/http'; | ||
3 | import { Router } from '@angular/router'; | ||
4 | import { Observable } from 'rxjs/Observable'; | ||
5 | import { Subject } from 'rxjs/Subject'; | ||
6 | |||
7 | import { AuthStatus } from './auth-status.model'; | ||
8 | import { AuthUser } from './auth-user.model'; | ||
9 | import { RestExtractor } from '../rest'; | ||
10 | |||
11 | @Injectable() | ||
12 | export class AuthService { | ||
13 | private static BASE_CLIENT_URL = '/api/v1/clients/local'; | ||
14 | private static BASE_TOKEN_URL = '/api/v1/users/token'; | ||
15 | private static BASE_USER_INFORMATIONS_URL = '/api/v1/users/me'; | ||
16 | |||
17 | loginChangedSource: Observable<AuthStatus>; | ||
18 | |||
19 | private clientId: string; | ||
20 | private clientSecret: string; | ||
21 | private loginChanged: Subject<AuthStatus>; | ||
22 | private user: AuthUser = null; | ||
23 | |||
24 | constructor( | ||
25 | private http: Http, | ||
26 | private restExtractor: RestExtractor, | ||
27 | private router: Router | ||
28 | ) { | ||
29 | this.loginChanged = new Subject<AuthStatus>(); | ||
30 | this.loginChangedSource = this.loginChanged.asObservable(); | ||
31 | |||
32 | // Fetch the client_id/client_secret | ||
33 | // FIXME: save in local storage? | ||
34 | this.http.get(AuthService.BASE_CLIENT_URL) | ||
35 | .map(this.restExtractor.extractDataGet) | ||
36 | .catch((res) => this.restExtractor.handleError(res)) | ||
37 | .subscribe( | ||
38 | result => { | ||
39 | this.clientId = result.client_id; | ||
40 | this.clientSecret = result.client_secret; | ||
41 | console.log('Client credentials loaded.'); | ||
42 | }, | ||
43 | 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 | ); | ||
48 | } | ||
49 | ); | ||
50 | |||
51 | // Return null if there is nothing to load | ||
52 | this.user = AuthUser.load(); | ||
53 | } | ||
54 | |||
55 | getRefreshToken() { | ||
56 | if (this.user === null) return null; | ||
57 | |||
58 | return this.user.getRefreshToken(); | ||
59 | } | ||
60 | |||
61 | getRequestHeaderValue() { | ||
62 | return `${this.getTokenType()} ${this.getAccessToken()}`; | ||
63 | } | ||
64 | |||
65 | getAccessToken() { | ||
66 | if (this.user === null) return null; | ||
67 | |||
68 | return this.user.getAccessToken(); | ||
69 | } | ||
70 | |||
71 | getTokenType() { | ||
72 | if (this.user === null) return null; | ||
73 | |||
74 | return this.user.getTokenType(); | ||
75 | } | ||
76 | |||
77 | getUser(): AuthUser { | ||
78 | return this.user; | ||
79 | } | ||
80 | |||
81 | isAdmin() { | ||
82 | if (this.user === null) return false; | ||
83 | |||
84 | return this.user.isAdmin(); | ||
85 | } | ||
86 | |||
87 | isLoggedIn() { | ||
88 | if (this.getAccessToken()) { | ||
89 | return true; | ||
90 | } else { | ||
91 | return false; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | login(username: string, password: string) { | ||
96 | let body = new URLSearchParams(); | ||
97 | body.set('client_id', this.clientId); | ||
98 | body.set('client_secret', this.clientSecret); | ||
99 | body.set('response_type', 'code'); | ||
100 | body.set('grant_type', 'password'); | ||
101 | body.set('scope', 'upload'); | ||
102 | body.set('username', username); | ||
103 | body.set('password', password); | ||
104 | |||
105 | let headers = new Headers(); | ||
106 | headers.append('Content-Type', 'application/x-www-form-urlencoded'); | ||
107 | |||
108 | let options = { | ||
109 | headers: headers | ||
110 | }; | ||
111 | |||
112 | return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) | ||
113 | .map(this.restExtractor.extractDataGet) | ||
114 | .map(res => { | ||
115 | res.username = username; | ||
116 | return res; | ||
117 | }) | ||
118 | .flatMap(res => this.fetchUserInformations(res)) | ||
119 | .map(res => this.handleLogin(res)) | ||
120 | .catch((res) => this.restExtractor.handleError(res)); | ||
121 | } | ||
122 | |||
123 | logout() { | ||
124 | // TODO: make an HTTP request to revoke the tokens | ||
125 | this.user = null; | ||
126 | |||
127 | AuthUser.flush(); | ||
128 | |||
129 | this.setStatus(AuthStatus.LoggedOut); | ||
130 | } | ||
131 | |||
132 | refreshAccessToken() { | ||
133 | console.log('Refreshing token...'); | ||
134 | |||
135 | const refreshToken = this.getRefreshToken(); | ||
136 | |||
137 | let body = new URLSearchParams(); | ||
138 | body.set('refresh_token', refreshToken); | ||
139 | body.set('client_id', this.clientId); | ||
140 | body.set('client_secret', this.clientSecret); | ||
141 | body.set('response_type', 'code'); | ||
142 | body.set('grant_type', 'refresh_token'); | ||
143 | |||
144 | let headers = new Headers(); | ||
145 | headers.append('Content-Type', 'application/x-www-form-urlencoded'); | ||
146 | |||
147 | let options = { | ||
148 | headers: headers | ||
149 | }; | ||
150 | |||
151 | return this.http.post(AuthService.BASE_TOKEN_URL, body.toString(), options) | ||
152 | .map(this.restExtractor.extractDataGet) | ||
153 | .map(res => this.handleRefreshToken(res)) | ||
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 | }); | ||
169 | } | ||
170 | |||
171 | private fetchUserInformations (obj: any) { | ||
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 | ); | ||
185 | } | ||
186 | |||
187 | private handleLogin (obj: any) { | ||
188 | const id = obj.id; | ||
189 | const username = obj.username; | ||
190 | const role = obj.role; | ||
191 | const hashTokens = { | ||
192 | access_token: obj.access_token, | ||
193 | token_type: obj.token_type, | ||
194 | refresh_token: obj.refresh_token | ||
195 | }; | ||
196 | |||
197 | this.user = new AuthUser({ id, username, role }, hashTokens); | ||
198 | this.user.save(); | ||
199 | |||
200 | this.setStatus(AuthStatus.LoggedIn); | ||
201 | } | ||
202 | |||
203 | private handleRefreshToken (obj: any) { | ||
204 | this.user.refreshTokens(obj.access_token, obj.refresh_token); | ||
205 | this.user.save(); | ||
206 | } | ||
207 | |||
208 | private setStatus(status: AuthStatus) { | ||
209 | this.loginChanged.next(status); | ||
210 | } | ||
211 | |||
212 | } | ||
diff --git a/client/src/app/shared/auth/index.ts b/client/src/app/shared/auth/index.ts index ebd9e14cd..ce0bd8adf 100644 --- a/client/src/app/shared/auth/index.ts +++ b/client/src/app/shared/auth/index.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | export * from './auth-http.service'; | 1 | export * from './auth-http.service'; |
2 | export * from './auth-status.model'; | 2 | export * from './auth-status.model'; |
3 | export * from './auth.service'; | ||
4 | export * from './auth-user.model'; | 3 | export * from './auth-user.model'; |
diff --git a/client/src/app/shared/index.ts b/client/src/app/shared/index.ts index af34b4b64..52a647b83 100644 --- a/client/src/app/shared/index.ts +++ b/client/src/app/shared/index.ts | |||
@@ -3,3 +3,4 @@ export * from './forms'; | |||
3 | export * from './rest'; | 3 | export * from './rest'; |
4 | export * from './search'; | 4 | export * from './search'; |
5 | export * from './users'; | 5 | export * from './users'; |
6 | export * from './shared.module'; | ||
diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts new file mode 100644 index 000000000..141922322 --- /dev/null +++ b/client/src/app/shared/shared.module.ts | |||
@@ -0,0 +1,62 @@ | |||
1 | import { NgModule } from '@angular/core'; | ||
2 | import { CommonModule } from '@angular/common'; | ||
3 | import { HttpModule } from '@angular/http'; | ||
4 | import { FormsModule, ReactiveFormsModule } from '@angular/forms'; | ||
5 | import { RouterModule } from '@angular/router'; | ||
6 | |||
7 | import { BytesPipe } from 'angular-pipes/src/math/bytes.pipe'; | ||
8 | import { DropdownModule } from 'ng2-bootstrap/components/dropdown'; | ||
9 | import { ProgressbarModule } from 'ng2-bootstrap/components/progressbar'; | ||
10 | import { PaginationModule } from 'ng2-bootstrap/components/pagination'; | ||
11 | import { ModalModule } from 'ng2-bootstrap/components/modal'; | ||
12 | import { FileUploadModule } from 'ng2-file-upload/ng2-file-upload'; | ||
13 | |||
14 | import { AUTH_HTTP_PROVIDERS } from './auth'; | ||
15 | import { RestExtractor, RestService } from './rest'; | ||
16 | import { SearchComponent, SearchService } from './search'; | ||
17 | |||
18 | @NgModule({ | ||
19 | imports: [ | ||
20 | CommonModule, | ||
21 | FormsModule, | ||
22 | ReactiveFormsModule, | ||
23 | HttpModule, | ||
24 | RouterModule, | ||
25 | |||
26 | DropdownModule, | ||
27 | FileUploadModule, | ||
28 | ModalModule, | ||
29 | PaginationModule, | ||
30 | ProgressbarModule | ||
31 | ], | ||
32 | |||
33 | declarations: [ | ||
34 | BytesPipe, | ||
35 | SearchComponent | ||
36 | ], | ||
37 | |||
38 | exports: [ | ||
39 | CommonModule, | ||
40 | FormsModule, | ||
41 | ReactiveFormsModule, | ||
42 | HttpModule, | ||
43 | RouterModule, | ||
44 | |||
45 | DropdownModule, | ||
46 | FileUploadModule, | ||
47 | ModalModule, | ||
48 | PaginationModule, | ||
49 | ProgressbarModule, | ||
50 | BytesPipe, | ||
51 | |||
52 | SearchComponent | ||
53 | ], | ||
54 | |||
55 | providers: [ | ||
56 | AUTH_HTTP_PROVIDERS, | ||
57 | RestExtractor, | ||
58 | RestService, | ||
59 | SearchService | ||
60 | ] | ||
61 | }) | ||
62 | export class SharedModule { } | ||