keygen.component.ts 5.88 KB
Newer Older
1
2
import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
3
4
import {  map, switchMap, filter, catchError, tap, skip, take, delay } from 'rxjs/operators'
import { Observable, fromEvent, combineLatest, of, pipe, timer, throwError, from } from 'rxjs';
5
import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
Chris Hines's avatar
WIP    
Chris Hines committed
6
7
8

import * as forge from "node-forge";

9
10
import {BackendSelectionService} from '../backend-selection.service';
import { AuthToken, SshAuthzServer, KeyCert } from '../identity';
Chris Hines's avatar
Chris Hines committed
11
import { AuthorisationService } from '../authorisation.service';
12
import { APIServer } from '../apiserver';
13
import * as jwktossh from "jwk-to-ssh";
Chris Hines's avatar
Chris Hines committed
14
15
16
17
18
19
20


@Component({
  selector: 'app-keygen',
  templateUrl: './keygen.component.html',
  styleUrls: ['./keygen.component.css']
})
21
22
23
export class KeygenComponent implements OnInit, OnDestroy {

  private keygenWorker: Worker
Chris Hines's avatar
Chris Hines committed
24

25
26
27
28
29
30
  constructor(private route: ActivatedRoute, 
    private router: Router, 
    private authService: AuthorisationService, 
    private backendSelectionService: BackendSelectionService,
    private http: HttpClient)
  { }
Chris Hines's avatar
Chris Hines committed
31
32

  ngOnInit() {
33
34
35
36
    this.initPipelines();
  }

  ngOnDestroy() {
37
    //this.keygenWorker.terminate()
Chris Hines's avatar
WIP    
Chris Hines committed
38
39
40
41
  }
 
  initPipelines() {

42
43
44
    const token$: Observable<AuthToken> = this.route.fragment.pipe(
      map((v) => this.extractToken(v)),
    );
45
46
47
48
49
50
51
52
53
54
55
56
57
    const key$ = from(window.crypto.subtle.generateKey(
      {
        name: "ECDSA",
        namedCurve: "P-256",
      },
      true,
      ["sign","verify"]
    )).pipe(
      switchMap((v) => { return combineLatest([from(window.crypto.subtle.exportKey("jwk",v.privateKey)),from(window.crypto.subtle.exportKey("jwk",v.publicKey))]) }),
      map(([key,pub]) => {
        return {'private': jwktossh.pack({'jwk': key, 'comment': '', 'public': false})+"\n", 'public': jwktossh.pack({'jwk': pub, 'comment': '', 'public': true})+"\n"};
      }),
    )
58
59
60
61
62
63
64
65
66
67
68
69
70
71
    const apiserver$: Observable<APIServer> = this.backendSelectionService.apiserver.pipe(
      filter((v) => v !== undefined),filter((v) => v !== null),
    );

    let keycert$ = combineLatest([token$, key$, apiserver$]).pipe(
      switchMap(([token,key,apiserver]) => this.getCert(token,key,apiserver),
        ([token,key,apiserver],cert) => [key,cert,token]),
      tap(([key,cert,token]) => this.logout(token.sshauthzservice)),
    );

    let agent$ = combineLatest([keycert$.pipe(filter((v) => v !== null)),apiserver$]).pipe(
      switchMap(([keycert,apiserver]) => this.addCert(keycert,apiserver)),
      switchMap((_) => this.authService.updateAgentContents()),
      switchMap((_) => this.authService.loggedInAuthZ),
72
73
74
      switchMap((_) => of([null])),
    );
    agent$.subscribe( (res) => this.router.navigate([sessionStorage.getItem('path')]),
Chris Hines's avatar
Chris Hines committed
75
76
77
78
79
80
81
                      (err) => { console.log(err) ; 
                                 if (err.sshauthzservice !== undefined ) {
                                    this.logout(err.sshauthzservice)
                                    this.router.navigate(['/noaccount',err.sshauthzservice.name])
                                 } else {
                                    this.router.navigate(['/login'])}
                                 } )
Chris Hines's avatar
Chris Hines committed
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
 extractToken(frag: string) {
   if (frag === undefined || frag == null) {
       return;
   }
   let tokenmatch = null;
   let statematch = null;
   if (!(frag === undefined) && !(frag == null)) {
     tokenmatch = frag.match(/access_token\=([^&]+)(&|$)/);
     statematch = frag.match(/state\=([^&]+)(&|$)/);
   }
   if (tokenmatch == null || statematch == null) {
     throw new Error('no token present');
   }

   let accesstoken = tokenmatch[1];
   let state = statematch[1];
   let tuple = JSON.parse(sessionStorage.getItem('authservice'));
   if (tuple[1] != state) {
     throw new Error('callback state parameter does not match');
   }

   return new AuthToken(tokenmatch[1],tuple[0]);

 }

  logout(sshauthzserver) {
Chris Hines's avatar
Chris Hines committed
110
   if (sshauthzserver !== undefined && sshauthzserver.logout !== null) {
111
112
113
     window.open(sshauthzserver.logout);
   }
  }
Chris Hines's avatar
WIP    
Chris Hines committed
114

115
  getCert(token: AuthToken, key: any, apiserver: APIServer): Observable<any> {
116
    let headers = new HttpHeaders({'Authorization':'Bearer '+token.token});
117
    let options = { headers: headers, withCredentials: false};
118
119
120
121
122
123
    var now = new Date()
    var end = new Date(now.getTime() + 24*60*60*1000); //request a certificate valid for 24 hours
    let data = {'public_key': key.public, 'end': end.toISOString()};
    return this.http.post<any>(token.sshauthzservice.sign,data, options).pipe(
      tap((v) => console.log('in getCert',v)),
      map((v) => v.certificate),
Chris Hines's avatar
Chris Hines committed
124
      catchError((e) => { console.error(e); return throwError(token) })
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    )
  }

  addCert(kclist: any, apiserver: APIServer): Observable<any> {
    let keyCert = new KeyCert()
    keyCert.key = kclist[0].private
    keyCert.cert = kclist[1]
    return this.sshAdd(keyCert,apiserver);
  }

 public sshAdd(keyCert: KeyCert, apiserver) {
   let headers = new HttpHeaders();
   let options = { headers: headers, withCredentials: true};
   var anyvar: any;
   let data = {'key': keyCert.key, 'cert': keyCert.cert};
   return this.http.post<any>(apiserver.tes+'/sshagent',data,options)
 }

  storeKey(keyCert: KeyCert) {
    var keys: KeyCert[] = [];
    try{
      keys = JSON.parse(sessionStorage.getItem('keys'));
    } catch {
      keys = [];
    }
    if (keys === null) {
      keys = [];
    }
    keys.push(keyCert);
    sessionStorage.setItem('keys',JSON.stringify(keys))
  }


Chris Hines's avatar
WIP    
Chris Hines committed
158
    /*  ngAfterViewInit() {
Chris Hines's avatar
Chris Hines committed
159
    this.authService.getCert();
Chris Hines's avatar
Chris Hines committed
160
161
  }

Chris Hines's avatar
WIP    
Chris Hines committed
162
163
164
165
166
167
168
169
  genKey() {
   let newkeypair = keypair();
   let publicKey = forge.pki.publicKeyFromPem(newkeypair.public);
   let sshpub = forge.ssh.publicKeyToOpenSSH(publicKey, 'sv2@monash.edu');
   return sshpub
  }*/

    /*  navigate(readyToNavigate: [Boolean,string]) {
Chris Hines's avatar
Chris Hines committed
170
171
172
    if (readyToNavigate[0]) {
      this.router.navigate([readyToNavigate[1]]);
    }
Chris Hines's avatar
WIP    
Chris Hines committed
173
  }*/
Chris Hines's avatar
Chris Hines committed
174

Chris Hines's avatar
WIP    
Chris Hines committed
175
  }