user.service.ts 10.1 KB
Newer Older
Lukas Burgey's avatar
Lukas Burgey committed
1
2
import {Injectable} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
Lukas Burgey's avatar
Lukas Burgey committed
3
4
import {MatTableDataSource} from '@angular/material';

Lukas Burgey's avatar
Lukas Burgey committed
5
import {Observable} from 'rxjs/Observable';
6
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
7
import {Subject} from 'rxjs/Subject';
Lukas Burgey's avatar
Lukas Burgey committed
8
9

import {CookieService} from 'ngx-cookie-service';
10
11
import {StompConfig, StompRService} from '@stomp/ng2-stompjs';
import {Message} from '@stomp/stompjs';
Lukas Burgey's avatar
Lukas Burgey committed
12

Lukas Burgey's avatar
Lukas Burgey committed
13
import {SnackBarService} from './snackbar.service';
14
import {IdpService} from './idp.service';
15
16
// import * as t from './types/types.module';
import { User, Update, State, Deployment, SSHKey, NewSSHKey, Group, IdP, Service } from './types/types.module';
Lukas Burgey's avatar
Lukas Burgey committed
17

Lukas Burgey's avatar
Lukas Burgey committed
18
19
20

@Injectable()
export class UserService {
21
22
  private initialized = false;
  private loggedIn: boolean = false;
Lukas Burgey's avatar
Lukas Burgey committed
23
  private loggedIn$ = new BehaviorSubject<boolean>(false);
Lukas Burgey's avatar
Lukas Burgey committed
24

25
26
  private user: User;
  private user$ = <BehaviorSubject<User>> new BehaviorSubject(undefined);
Lukas Burgey's avatar
Lukas Burgey committed
27

28
29
  private sshKeys: SSHKey[] = new Array<SSHKey>();
  private sshKeys$ = new BehaviorSubject<SSHKey[]>(this.sshKeys);
Lukas Burgey's avatar
Lukas Burgey committed
30

31
32
  private deployments: Map<number, Deployment> = new Map([]);
  private deployments$ = <BehaviorSubject<Deployment[]>> new BehaviorSubject([]);
Lukas Burgey's avatar
Lukas Burgey committed
33

34
35
  public groupSelector = (user: User) => user ?  user.groups ? user.groups : [] : [];
  public serviceSelector = (user: User) => user ? user.services ? user.services : [] : [];
Lukas Burgey's avatar
Lukas Burgey committed
36
37

  constructor(
Lukas Burgey's avatar
Lukas Burgey committed
38
39
40
41
    private cookieService: CookieService,
    private http: HttpClient,
    private snackBar: SnackBarService,
    private idpService: IdpService,
42
    private _stompService: StompRService,
Lukas Burgey's avatar
Lukas Burgey committed
43
  ) {
44
    this.connect();
45
46
  }

47
48
49
50
  // PRIVATE API
  private disconnect(): void {
    this.loggedIn = false;
    this.sshKeys$.complete();
51

52
    this.loggedIn$.next(this.loggedIn);
Lukas Burgey's avatar
Lukas Burgey committed
53
54
  }

55
  private connect(): void {
56
57
    this.subscribeUser().subscribe(
      (newUser: User) => {
58
        // update users
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
        if (newUser != undefined) {

          if (newUser.id) {
            this.connectLiveUpdates(newUser.id);
          }
          if (newUser.ssh_keys) {
            this.sshKeys = newUser.ssh_keys;
            this.sshKeys$.next(this.sshKeys);
          }
          if (newUser.deployments) {
            newUser.deployments.forEach(
              (newDep: Deployment) => {
                this.deployments.set(newDep.id,  newDep);
              }
            );
            this.deployments$.next(Array.from(this.deployments.values()));
          }

77
        }
Lukas Burgey's avatar
Lukas Burgey committed
78

79
      },
80
81
      this.logErrorAndFetch,
      () => console.log('user$ is complete'),
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
    );

    this.subscribeLoggedIn().subscribe(
      (loggedIn: boolean) => {

        if (loggedIn) {

          if (!this.initialized) {
            this.snackBar.open('Logged in');
          }
          this.initialized = true;
        }
        if (!loggedIn && this.initialized) {
          console.log('Logging out');
          this._stompService.disconnect();

98
99
          this.user$.complete();
          this.deployments$.complete();
100
101
102
103
104
          this.sshKeys$.complete();

          this.snackBar.open('Logged out');
        }
      },
105
      this.logErrorAndFetch,
106
107
108
109
110
      () => console.log('loggedIn$ completed'),
    );

    // start pulling updates
    this.fetch();
Lukas Burgey's avatar
Lukas Burgey committed
111
112
  }

113
  private connectLiveUpdates(userID: number): void {
Lukas Burgey's avatar
Lukas Burgey committed
114
    // handle with care
115
    let login = userID
Lukas Burgey's avatar
Lukas Burgey committed
116
    let passcode = this.cookieService.get('sessionid');
Lukas Burgey's avatar
Lukas Burgey committed
117

Lukas Burgey's avatar
Lukas Burgey committed
118
119
    const stompConfig: StompConfig = {
      // Which server?
120
      url: 'wss://'+window.location.host+'/ws',
121

Lukas Burgey's avatar
Lukas Burgey committed
122
123
124
125
126
127
      // Headers
      // Typical keys: login, passcode, host
      headers: {
        login: 'webpage-client:' + login,
        passcode: passcode,
      },
Lukas Burgey's avatar
Lukas Burgey committed
128

Lukas Burgey's avatar
Lukas Burgey committed
129
130
131
132
133
134
      // How often to heartbeat?
      // Interval in milliseconds, set to 0 to disable
      heartbeat_in: 0, // Typical value 0 - disabled
      heartbeat_out: 20000, // Typical value 20000 - every 20 seconds
      // Wait in milliseconds before attempting auto reconnect
      // Set to 0 to disable
135
136
      // Typical value 15000 (15 seconds)
      reconnect_delay: 15000,
137

Lukas Burgey's avatar
Lukas Burgey committed
138
139
140
141
142
      // Will log diagnostics on console
      debug: false,
    };
    this._stompService.config = stompConfig;
    this._stompService.initAndConnect();
Lukas Burgey's avatar
Lukas Burgey committed
143

Lukas Burgey's avatar
Lukas Burgey committed
144
    let subscription = this._stompService.subscribe(
145
      '/exchange/users/' + userID.toString()
Lukas Burgey's avatar
Lukas Burgey committed
146
    );
147

Lukas Burgey's avatar
Lukas Burgey committed
148
149
    subscription.subscribe(
      (message: Message) => {
150
151
152
        let update: Update = JSON.parse(message.body);
        console.log('update:', update);

Lukas Burgey's avatar
Lukas Burgey committed
153
        if (update.error && update.error != '') {
154
          console.log(update.error);
Lukas Burgey's avatar
Lukas Burgey committed
155
          this.snackBar.open(update.error);
Lukas Burgey's avatar
Lukas Burgey committed
156
        }
157
158
159

        if (update.deployment) {
          this.updateDeployment(update.deployment);
Lukas Burgey's avatar
Lukas Burgey committed
160
        }
Lukas Burgey's avatar
Lukas Burgey committed
161
      },
162
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
163
    );
Lukas Burgey's avatar
Lukas Burgey committed
164
  }
165

166
167
  private updateState(update: State) {
    console.log('updateState:', update);
Lukas Burgey's avatar
Lukas Burgey committed
168
169


170
171
172
    if (update) {
      // report an occured error
      if (update.error) {
173
174
        console.log(update.error);
        this.snackBar.open(update.error);
175
      }
Lukas Burgey's avatar
Lukas Burgey committed
176

177
178
179
180
181
182
183
184
185
      // did a login occur?
      let login = (!this.loggedIn && update.user);
      // did a logout occur?
      let logout = (this.loggedIn && !update.user);

      // -- Value updating --
      if (logout) {
        this.loggedIn = false;
        this.loggedIn$.next(this.loggedIn);
186
        return
187
      }
188
189
190
191
192
193
194
195
      if (login) {
        this.loggedIn = true;
        this.loggedIn$.next(this.loggedIn);
      }
      if (update.user) {
        this.user = update.user;
        this.user$.next(this.user);
      }
196
197
198
199

    } else {
      this.disconnect();
      return
Lukas Burgey's avatar
Lukas Burgey committed
200
201
202
    }
  }

203
  private fetch(): void {
Lukas Burgey's avatar
Lukas Burgey committed
204
    this.http.get('/backend/api/state').subscribe(
205
      (state: State) => this.updateState(state),
Lukas Burgey's avatar
Lukas Burgey committed
206
207
208
209
      (err: any) => {
        console.log('Error in fetch:', err);
        this.snackBar.open('Error. Retry sometime later');
      }
Lukas Burgey's avatar
Lukas Burgey committed
210
211
212
    );
  }

213
214
215
216
217
218
219
  private updateDeployment(dep: Deployment): void {
    if (dep) {
      this.deployments.set(dep.id, dep);
      this.deployments$.next(Array.from(this.deployments.values()));
    }
  }

220
  // PUBLIC API
221
  public serviceDescription(service: Service): string {
222
223
224
225
226
    if (service.description != "") {
      return service.description;
    }
    return "No description";
  }
Lukas Burgey's avatar
Lukas Burgey committed
227

228
229
230
  public getGroups(): Group[] {
    if (this.user.groups) {
      return this.user.groups.sort(
Lukas Burgey's avatar
Lukas Burgey committed
231
232
233
234
235
236
237
238
239
240
241
242
243
244
        function(a,b) {
          if (a.name < b.name) {
            return -1;
          } else if (a.name > b.name) {
            return 1;
          } else if (a.name === b.name) {
            return 0;
          }
        }
      );
    }
    return []
  }

245
246
  public getDeployment(service: Service): Deployment {
    return this.user.deployments.find(
247
248
      dep => dep.service && dep.service.id === service.id
    );
Lukas Burgey's avatar
Lukas Burgey committed
249
250
  }

251
  public getServices(group: Group): Service[] {
252
253
254
    // TODO implement
    //
    return []
Lukas Burgey's avatar
Lukas Burgey committed
255
256
  }

257
258
259
  public logErrorAndFetch(error: any)  {
    if (error.status === 403) {
      this.login();
Lukas Burgey's avatar
Lukas Burgey committed
260
    }
261
262
263
264
265

    console.log(error);
    this.snackBar.open('Error');
    this.fetch();
    return Observable.throw(error);
Lukas Burgey's avatar
Lukas Burgey committed
266
267
  }

268
269
270
271
  public login(idp?: IdP): void {
    if (idp) {
      this.idpService.setIdPPreference(idp);
    }
272

273
    window.location.href = '/backend/auth/v1/request';
274
275
  }

Lukas Burgey's avatar
Lukas Burgey committed
276
  public logout() {
277
    this.http.post('/backend/auth/v1/logout', {}).subscribe(
278
279
      (state: State) => this.updateState(state),
      this.logErrorAndFetch,
280
281
282
    );
  }

283
  public sentQuestionnaire(stateItemID: number, answers: Object) {
284
    return this.http.post('/backend/api/questionnaire?id='+String(stateItemID), answers).subscribe(
285
      (data: Deployment) => {
Lukas Burgey's avatar
Lukas Burgey committed
286
        this.snackBar.open('Uploaded questionnaire');
287
288
289
        /* TODO fix
        this.userUserUpdate(data);
        */
Lukas Burgey's avatar
Lukas Burgey committed
290
      },
291
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
292
293
294
    );
  }

Lukas Burgey's avatar
Lukas Burgey committed
295
  public deleteUser() {
Lukas Burgey's avatar
Lukas Burgey committed
296
297
298
    return this.http.delete('/backend/api/delete_user').subscribe(
      (data: {deleted: boolean}) => {
        if (data && data.deleted) {
299
          this.disconnect();
Lukas Burgey's avatar
Lukas Burgey committed
300
301
          this.snackBar.open('Deleted user from server');
        }
Lukas Burgey's avatar
Lukas Burgey committed
302
      },
303
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
304
305
    );
  }
Lukas Burgey's avatar
Lukas Burgey committed
306

307
  public uploadSshKey(key: NewSSHKey) {
Lukas Burgey's avatar
Lukas Burgey committed
308
309
310
311
312
313
    console.log('uploading key:', key);
    const body = {
      'type': 'add',
      'key': key,
    };
    return this.http.post('/backend/api/sshkey', body).subscribe(
314
      (newKey: SSHKey) => {
Lukas Burgey's avatar
Lukas Burgey committed
315
316
        this.sshKeys.push(newKey);
        this.sshKeys$.next(this.sshKeys);
317
      },
318
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
319
320
321
    );
  }

322
  public removeSshKey(key: SSHKey) {
Lukas Burgey's avatar
Lukas Burgey committed
323
324
325
326
327
328
329
330
331
332
333
334
    console.log('deleting key:', key);
    return this.http.post('/backend/api/sshkey', {
      'type': 'remove',
      'id': key.id,
    }).subscribe(
      (data: {deleted: boolean}) => {
        if (data && data.deleted) {
          this.sshKeys = this.sshKeys.filter(
            k => key.id != k.id
          );
          this.sshKeys$.next(this.sshKeys);
        }
335
      },
336
      this.logErrorAndFetch,
Lukas Burgey's avatar
Lukas Burgey committed
337
338
    );
  }
339

340
  public changeDeployment(action: string, group: Group): Observable<Deployment> {
341
342
343
344
    const body = {
      'type': action,
      'group': group.id,
    };
345
346
    return this.http.post<Deployment>('/backend/api/deployments', body).catch(
      this.logErrorAndFetch,
347
348
349
350
351
    );
  }

  // DATA SERVICE API
  //
352
  public subscribeSSHKeys(): Observable<SSHKey[]> {
353
354
355
356
357
358
359
    return this.sshKeys$.asObservable();
  }

  public subscribeLoggedIn(): Observable<boolean> {
    return this.loggedIn$.asObservable();
  }

360
361
  public subscribeUser(): Observable<User> {
    return this.user$.asObservable();
362
363
  }

364
365
  public subscribeDeployments(): Observable<Deployment[]> {
    return this.deployments$.asObservable();
366
367
  }

368
369
  public subscribeSpecific<T>(selector: (user: User) => T): Observable<T> {
    return this.subscribeUser().map(selector);
370
371
  }

372
373
374
375
376
  public subscribeServiceDeployment(service: Service): Observable<Deployment> {
    return this.subscribeDeployments().map(
      (deployments: Deployment[]) => {
          return deployments.find(
            (dep: Deployment) => dep.service ? dep.service.id == service.id : false
377
378
379
380
381
          );
        }
    );
  }

382
383
384
385
386
387
388
  public subscribeGroupDeployment(group: Group): Observable<Deployment> {
    return this.subscribeDeployments().map(
      (deployments: Deployment[]) => {
        return deployments.find(
          (dep: Deployment) => dep.group == group.id
        );
      }
389
390
    );
  }
Lukas Burgey's avatar
Lukas Burgey committed
391
}