aboutsummaryrefslogtreecommitdiff
path: root/Timeline/ClientApp/src/app/user-dialog/user.service.ts
blob: 1afebc91c95decb3341580279365dc118ec60551 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { map, catchError, retry } from 'rxjs/operators';

export interface UserCredentials {
  username: string;
  password: string;
}

export interface UserInfo {
  username: string;
  roles: string[];
}

export interface CreateTokenResult {
  token: string;
  userInfo: UserInfo;
}

export interface TokenValidationRequest {
  token: string;
}

export interface TokenValidationResult {
  isValid: boolean;
  userInfo?: UserInfo;
}

export interface UserLoginState {
  state: 'nologin' | 'invalid' | 'success';
  userInfo?: UserInfo;
}

export class BadNetworkException extends Error {
  constructor() {
    super('Network is bad.');
  }
}

export class AlreadyLoginException extends Error {
  constructor() {
    super('There is already a token saved. Please call validateUserLoginState first.');
  }
}

export class BadCredentialsException extends Error {
  constructor(username: string = null , password: string = null) {
    super(`Username[${username}] or password[${password}] is wrong.`);
  }
}

@Injectable({
  providedIn: 'root'
})
export class UserService {

  private token: string;
  private userInfo: UserInfo;

  constructor(private httpClient: HttpClient) { }

  validateUserLoginState(): Observable<UserLoginState> {
    if (this.token === undefined || this.token === null) {
      return of(<UserLoginState>{ state: 'nologin' });
    }

    return this.httpClient.post<TokenValidationResult>('/api/User/ValidateToken', <TokenValidationRequest>{ token: this.token }).pipe(
      retry(3),
      catchError(error => {
        console.error('Failed to validate token.');
        return throwError(error);
      }),
      map(result => {
        if (result.isValid) {
          this.userInfo = result.userInfo;
          return <UserLoginState>{
            state: 'success',
            userInfo: result.userInfo
          };
        } else {
          this.token = null;
          this.userInfo = null;
          return <UserLoginState>{
            state: 'invalid'
          };
        }
      })
    );
  }

  tryLogin(username: string, password: string): Observable<UserInfo> {
    if (this.token) {
      return throwError(new AlreadyLoginException());
    }

    return this.httpClient.post<CreateTokenResult>('/api/User/CreateToken', <UserCredentials>{
      username, password
    }).pipe(
      catchError((error: HttpErrorResponse) => {
        if (error.error instanceof ErrorEvent) {
          console.error('An error occurred when login: ' + error.error.message);
          return throwError(new BadNetworkException());
        } else if (error.status === 400) {
          console.error('An error occurred when login: wrong credentials.');
          return throwError(new BadCredentialsException(username, password));
        } else {
          console.error('An unknown error occurred when login: ' + error);
          return throwError(error);
        }
      }),
      map(result => {
        this.token = result.token;
        this.userInfo = result.userInfo;
        return result.userInfo;
      })
    );
  }
}