diff --git a/.gitignore b/.gitignore index 92ba930c3c845d19f6c0381172e7c735a4747002..fd006da959e1846dde839a58b9dc708c1ea8261f 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,20 @@ app.key key.pem openssl.conf cert.pem + + +app.csr +ca.cnf +ca.crt +ca.key +junk/ +pytest.py +src/app/.tes.service.ts.swn +src/app/Why Strudel2.md +src/assets/config/computesites.json.bk +src/assets/params.json +src/assets/uischema.json +src/deployments/mlerp/assets/config/combined.json +ssl.cnf +yarnlist.local + diff --git a/src/app/accountinfo/accountinfo.component.ts b/src/app/accountinfo/accountinfo.component.ts index 06bd7502d2a6e7524d73f37d1658fe80b55c8809..27e8d0fd8b0e97eceb02938473827f7de76b8226 100644 --- a/src/app/accountinfo/accountinfo.component.ts +++ b/src/app/accountinfo/accountinfo.component.ts @@ -25,7 +25,7 @@ export class AccountinfoComponent implements OnInit { } ngOnInit() { - this.identity.accountalerts.subscribe((v) => {console.log('account alerts',v)}); + //this.identity.accountalerts.subscribe((v) => {console.log('account alerts',v)}); } cssClass(h) { diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 6fd338881ccfee12e1c1df03428aab5538750253..d99e53500a166ca02de41cf2ab90c3fb97b7ec49 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -2,8 +2,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { LauncherComponent } from './launcher/launcher.component'; import { KeygenComponent } from './keygen/keygen.component'; -import { TransferComponent } from './transfer/transfer.component'; -import { ShareconnectComponent } from './shareconnect/shareconnect.component'; import { JoblistComponent } from './joblist/joblist.component'; import {LoginComponent} from './login/login.component'; @@ -33,7 +31,6 @@ const routes: Routes = [ //{ path: 'cancellaunch', component: LauncherComponent}, { path: 'sshauthz_callback', component: KeygenComponent}, { path: 'sshauthz_callback*', component: KeygenComponent}, - { path: 'transfer', component: TransferComponent }, { path: 'noaccount/:site', component: NoaccountComponent}, { path: '**', component: LauncherComponent}, //{ path: 'shareconnect', component: ShareconnectComponent } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 815b2b3d6141708d24889de2e00b29aa05702852..334a46ef35f4061051bbe74d33b258f9104a2a01 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -57,9 +57,6 @@ import { MatChipsModule } from '@angular/material/chips'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { StrudelapplistComponent } from './strudelapplist/strudelapplist.component'; import { KeygenComponent } from './keygen/keygen.component'; -import { TransferComponent } from './transfer/transfer.component'; -import { Sv2SideNavComponent } from './sv2-side-nav/sv2-side-nav.component'; -import { ShareconnectComponent } from './shareconnect/shareconnect.component'; import { LaunchDialogComponent } from './launch-dialog/launch-dialog.component'; import { ModaldialogComponent } from './modaldialog/modaldialog.component' import {OverlayModule} from '@angular/cdk/overlay'; @@ -97,9 +94,6 @@ import { LaunchformComponent } from './launchform/launchform.component'; // LaunchdialogComponent, StrudelapplistComponent, KeygenComponent, - TransferComponent, - Sv2SideNavComponent, - ShareconnectComponent, LaunchDialogComponent, ModaldialogComponent, LoginComponent, diff --git a/src/app/authorisation.service.ts b/src/app/authorisation.service.ts index 5b4b4e205f261fca549960b3d308b117cb895c8f..25b0f45a014ae3cf70d8126d13dc4a0966b1da41 100644 --- a/src/app/authorisation.service.ts +++ b/src/app/authorisation.service.ts @@ -1,7 +1,25 @@ +// Authoriations server +// On load checks for a session token +// if a sesiontoken exists get the loggedinauthz/loggedoutauthz observables +// +// exposes function updateFragment ... if the fragment is updated, +// 1. generate a new key +// 2. request a certificate +// 3. add the certificate to the agent +// 4. update the loggedin/loggedout data +// +// exposes function refresh() ... if called, recalculates loggedin/loggedout +// +// exposes storeSshAuthZServer ... if called adds a new entry to the possible loggedin/loggedout and stores in localStorage +// +// maintains behavioursubject loggedinauthz and loggedin (list and count of sites that we have logged in to/have a valid cert in the agent for) +// maintains behavioursubject loggedoutauthz and loggedout (list and count of sites that we could log in to) + + import { Injectable } from '@angular/core'; import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; import { ActivatedRoute, Router } from '@angular/router'; -import { Observable, Subject, BehaviorSubject, concat, forkJoin } from 'rxjs'; +import { Observable, Subject, BehaviorSubject, concat, forkJoin, ReplaySubject } from 'rxjs'; import { catchError, map, tap, take,filter,skip, switchMap, shareReplay } from 'rxjs/operators'; import {LocationStrategy, Location} from '@angular/common'; import { Identity, AuthToken, KeyCert, SshAuthzServer } from './identity'; @@ -20,136 +38,146 @@ export class SshauthzServer {} providedIn: 'root' }) export class AuthorisationService { - // public token: BehaviorSubject<AuthToken>; - //public readonly sshAuthzServers: BehaviorSubject<SshAuthzServer[]>; - //public readonly agentContents: BehaviorSubject<any>; - //public loggedInAuthZ: BehaviorSubject<SshAuthzServer[]>; - //public loggedOutAuthZ: BehaviorSubject<SshAuthzServer[]>; - public loggedInAuthZ$: Observable<SshAuthzServer[]>; - public loggedOutAuthZ$: Observable<SshAuthzServer[]>; - //public progress: Subject<string>; - // private keyCert: Subject<KeyCert>; - public backendURI: string; - public keys: KeyCert[]; - private fragment$: BehaviorSubject<string> = new BehaviorSubject(null); - public agentContents$: Observable<any>; - private backendURI$: Observable<string>; + + + public loggedInAuthZ$: BehaviorSubject<SshAuthzServer[]>; + public loggedOutAuthZ$: BehaviorSubject<SshAuthzServer[]>; public loggedin$: BehaviorSubject<number>; public loggedout$: BehaviorSubject<number>; - public sshAuthzServers$: Observable<SshAuthzServer[]>; - public refresh$: Subject<boolean> = new Subject(); - public reAdd$: Subject<any> = new Subject(); + public sessionToken: string | null = null + public availableKeys$: BehaviorSubject<any>; + + private backendURI: string; + private fragment$: BehaviorSubject<string> = new BehaviorSubject(null); + private sshAuthzServers$: BehaviorSubject<SshAuthzServer[]>; + private refresh$: Subject<boolean> = new Subject(); + + + constructor(private http: HttpClient, - private locationStrategy: LocationStrategy, private router: Router, private backendSelectionService: BackendSelectionService, - private location: Location, private notifications: NotificationsService, private ipcService: IpcService) { - - //this.sshAuthzServers = new BehaviorSubject<SshAuthzServer[]>([]); - //this.loggedInAuthZ = new BehaviorSubject<SshAuthzServer[]>(null); - //this.loggedOutAuthZ = new BehaviorSubject<SshAuthzServer[]>([]); - this.backendURI = null; - //this.agentContents = new BehaviorSubject(null); - this.keys = []; - //this.getSshAuthzServers(); - this.keys = []; - - this.sshAuthzServers$ = this.getSshAuthzServersObservable(); + + this.backendURI = null; + this.sshAuthzServers$ = new BehaviorSubject<SshAuthzServer[]>(null); this.loggedin$= new BehaviorSubject<number>(null); this.loggedout$= new BehaviorSubject<number>(null); - this.backendSelectionService.apiserver.pipe( - filter((v) => v !== null && v !== undefined), - ) - .subscribe((value) => { this.backendURI = value.tes }); // Once we have a value for backend, store that value locally + this.loggedInAuthZ$ = new BehaviorSubject<any>([]); + this.loggedOutAuthZ$ = new BehaviorSubject<any>([]); + this.availableKeys$ = new BehaviorSubject<any>([]); - // // Once we have backend server, check what our ssh agent has in it - // this.backendSelectionService.apiserver.pipe( - // filter((v) => v !== null && v !== undefined), - // tap(() => console.log('backendSelectionService fired causing agent contents to update')), - // switchMap((v) => this.updateAgentContents(v.tes)), - // ).subscribe((_) => { return }, (err) => console.error(err)); // An empty subscription is necessary for the observables to fire + + // Get the list of configured sshauthz servers + this.getSshAuthzServersObservable().subscribe((v) => this.sshAuthzServers$.next(v)) - this.backendURI$ = this.backendSelectionService.apiserver.pipe( + // every time the backend changes, refresh the list of certs in the agent + this.backendSelectionService.apiserver.pipe( filter((v) => v !== null && v !== undefined), - map((v) => (<APIServer>v).tes) ) - - this.agentContents$ = this.backendURI$.pipe( - tap((v) => console.log('backendURI changed, causing agent contents to update',v)), - switchMap((v) => this.updateAgentContents(v)), + .subscribe((value) => { this.backendURI = value.tes ; this.refresh() }); // Once we have a value for backend, store that value locally + this.backendSelectionService.apiserver.pipe( filter((v) => v !== null && v !== undefined), - shareReplay() - ) - - - /* Once we have a value for agent Conents, and a list of servers, we can figure out which ones we hav elogged into and which ones we haven't */ - - var authZ$: Observable<any>; - authZ$ = combineLatest([this.agentContents$,this.sshAuthzServers$.pipe(filter((v) => v !== null && v !== undefined))]).pipe( + take(1), + switchMap((v) => this.getSessionToken(v)) + ).subscribe((v) => { this.sessionToken = v; window.localStorage.setItem('strudel2_session', v); this.refresh() } ); + + // every time the list of certs/keys changes update the list of whats logged in and out + // sshAuthzServers$ won't really "change" except that it needs to be retrieved first time + combineLatest([this.availableKeys$, this.sshAuthzServers$.pipe(filter((v) => v !== null ))]).pipe( map(([agentContents,authzServers]) => { return this.updateLoggedAuthZ(agentContents,authzServers)}), - tap((v) => { this.loggedin$.next(v[0].length); this.loggedout$.next(v[1].length)} ), - catchError((e) => { console.error('errort getting values for logged in and out',e); return throwError(e) }), - tap((v) => console.log('authZ observable fired')), - shareReplay() - ); - //authZ$.subscribe(([loggedin,loggedout]) => { this.loggedInAuthZ.next(loggedin); this.loggedOutAuthZ.next(loggedout); }) - this.loggedInAuthZ$ = authZ$.pipe(map((v) => v[0])); - this.loggedOutAuthZ$ = authZ$.pipe(map((v) => v[1])); + ).subscribe((v) => {this.loggedInAuthZ$.next(v[0]); this.loggedin$.next(v[0].length) ; this.loggedOutAuthZ$.next(v[1]); this.loggedout$.next(v[1].length)}) + - const apiserver$: Observable<APIServer> = this.backendSelectionService.apiserver.pipe( - filter((v) => v !== undefined),filter((v) => v !== null), - ); - combineLatest([this.reAdd$.pipe( - filter((v) => v !== undefined && v !== null)), apiserver$]).subscribe( - ([id, apiserver]) => { this.addKeysFromStorage(apiserver)}) - this.initKeygenPipelines(); } - private addKeysFromStorage(apiserver: APIServer) { - var req: Observable<any>[] = [] - var keys: KeyCert[]; - keys = this.getKeys(); - if (keys.length>0) { - for (let k of this.getKeys()) { - console.log('addKeysFromStorage',k); - req.push(this.sshAdd(k,apiserver)) - } - forkJoin(req).subscribe((v) => { this.refresh$.next(true)}) +// private addKeysFromStorage(apiserver: APIServer) { +// var req: Observable<any>[] = [] +// var keys: KeyCert[]; +// keys = this.getKeys(); +// if (keys.length>0) { +// for (let k of this.getKeys()) { +// console.log('addKeysFromStorage',k); +// req.push(this.sshAdd(k,apiserver)) +// } +// forkJoin(req).subscribe((v) => { this.refresh$.next(true)}) +// } else { +// this.refresh$.next(true); +// } +// } + + + public refresh() { + this.refresh$.next(true); + if (this.backendURI !== null) { + this.updateAgentContents(this.backendURI).subscribe((v) => this.availableKeys$.next(v)); } else { - this.refresh$.next(true); + this.backendSelectionService.apiserver.pipe( + take(1) + ).subscribe((apiserver) => this.updateAgentContents(apiserver.tes).subscribe((v) => this.availableKeys$.next(v))) } } - - updateFragment(frag) { - this.fragment$.next(frag); - } - storeLocalAuthZ(authz: any) { + public storeLocalAuthZ(authz: any) { + // called from settings try { localStorage.setItem('localauthservers',JSON.stringify(authz)); } catch { } - //this.getSshAuthzServers(); } - removeLocalAuthZ() { + public removeLocalAuthZ() { localStorage.removeItem('localauthservers'); - //this.getSshAuthzServers(); } -/* getSshAuthzServers() { - let headers = new HttpHeaders(); - let options = { headers: headers, withCredentials: false}; - this.http.get<SshAuthzServer[]>('./assets/config/authservers.json',options) - .pipe(catchError(this.handleError('getSshAuthzServers'))) - .subscribe(resp => this.updateSshAuthzServers(resp)); - } */ - getSshAuthzServersObservable(): Observable<SshAuthzServer[]> { +public updateFragment(frag) { + // The second half of login. After we have returned from the authorization server + // called from keygen + this.fragment$.next(frag); + } + + +public logout() { + // clear the ssh agent and remove all keys + // remove the sessionToken from localstorage and memory + // get a new session token + sessionStorage.removeItem('keys'); + return this.killAgent().pipe( + tap((v)=>window.localStorage.removeItem('strudel2_session')), + tap((v) => this.sessionToken = null), + switchMap((v) => this.getSessionToken(this.backendURI)) + ). + subscribe((v) =>{ this.sessionToken = v ; window.localStorage.setItem('strudel2_session',v) ; this.refresh() } ) +} + +private getSessionToken(apiserver: any): Observable<any> { + console.log('getting the session token eithe rfrom loca sotrage or a new one') + + let access_token = window.localStorage.getItem('strudel2_session'); + if (access_token) { + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${access_token}` + }) + let options = { headers: headers, withCredentials: true}; + let pipe = this.http.get<any>(apiserver.tes+'/authping', options).pipe( + tap((v) => console.log('found a token, refreshing it')), + switchMap((v) => this.http.get<any>(apiserver.tes+'/refreshsession', options)), + tap((v) => console.log('refreshed the token')), + catchError((e) => this.http.get<any>(apiserver.tes+'/newsession')) + ) + return pipe + } else { + console.log('getting a new token from', apiserver.tes) + return this.http.get<any>(apiserver.tes+'/newsession') + } +} + + + private getSshAuthzServersObservable(): Observable<SshAuthzServer[]> { let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: false}; return this.http.get<SshAuthzServer[]>('./assets/config/authservers.json',options) @@ -203,69 +231,29 @@ export class AuthorisationService { return [loggedin, loggedout] } +// private getKeys(id?: Identity): KeyCert[] { +// try{ +// return JSON.parse(sessionStorage.getItem('keys')); +// } catch { +// return []; +// } +// } - updateSshAuthzServers(resp) { - var auths: SshAuthzServer[]; - var localauths: SshAuthzServer[] = []; - var server: SshAuthzServer; - auths = <SshAuthzServer[]>resp; - try { - localauths = JSON.parse(localStorage.getItem('localauthservers')) - } catch { - localauths = [] - } - if (localauths !== null) { - for (server of localauths) { - auths.push(server); - } - } - //this.sshAuthzServers.next(auths); - } -public getKeys(id?: Identity): KeyCert[] { - try{ - return JSON.parse(sessionStorage.getItem('keys')); - } catch { - return []; - } -} - /* makeKeyCert(key: string, resp, sshauthzservice: SshAuthzServer) { - let keyCert = new KeyCert() - keyCert.key = key; - keyCert.cert = resp['cert']; - 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)) - // this.tesService.keyCert.next(keyCert); - // As soon as the certificate has been generated, we log back out of the signing server - if (!(sshauthzservice.logout === null)) { - window.open(sshauthzservice.logout); - } - let path=sessionStorage.getItem('path'); - //skip1 because loggedInAuthZ is a behaviour subject and we don't want the current value but the value - this.loggedInAuthZ.pipe(skip(1),take(1)).subscribe( () => {this.readyToNavigate.next([true,path])}); - this.sshAdd(keyCert); - // only navigate once the agent contents has been refreshed - }*/ - - public querySshAgentError(error: any) { + private querySshAgentError(error: any) { //this.agentContents.next([]); if (error.status == 0) { this.notifications.notify("A network error occured. Are you connected to the internet?") } + if (error.stats == 401) { + this.sessionToken = null; + console.log('session expired, why hasnt it refreshed automatically?') + } this.notifications.notify("Error querying ssh agent"); } - public updateAgentContents(apiserver?: string): Observable<any> { + private updateAgentContents(apiserver: string): Observable<any> { /* Query ssh agent running on the apiserver * Tap the even stream to update the notifications */ @@ -282,24 +270,20 @@ public getKeys(id?: Identity): KeyCert[] { } } - let headers = new HttpHeaders(); + if (this.sessionToken === null) { + return of([]) + } + + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.sessionToken}` + }); let options = { headers: headers, withCredentials: true}; - var anyvar: any; let agentquery$ = this.http.get<any>(apiserver+'/sshagent',options) let agentpipe$ = agentquery$.pipe( catchError((e) => { this.querySshAgentError(e); return of([])}), - switchMap((resp) => of(this.addExpiryField(resp))), - tap((resp) => { - // if (this.agentContents.value !== null && this.agentContents.value.length > resp.length) { - // this.notifications.notify("Your login expired. Please login again"); - // } else { - // this.notifications.notify(""); - // }; - this.notifications.notify(""); - //this.agentContents.next(resp) - }), + map((v) => this.addExpiryField(v)), + tap((resp) => { this.notifications.notify("");}), catchError((e) => { console.error('updateAgentContents error',e) ; return of([])}), - //tap((_) => this.notifications.notify("")) tap((v)=>console.log('agent contents',v)) ) return agentpipe$ @@ -320,41 +304,23 @@ public getKeys(id?: Identity): KeyCert[] { private killAgent() { this.notifications.notify("Logging out") - let headers = new HttpHeaders(); - let options = { headers: headers, withCredentials: true}; var anyvar: any; + if (this.sessionToken === null) { + return throwError('cant get the agent contents without a session'); + } + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.sessionToken}` + }); + let options = { headers: headers, withCredentials: true}; + return this.http.delete<any>(this.backendURI+'/sshagent',options) .pipe( catchError(this.handleError(anyvar)), - switchMap((v) => this.updateAgentContents())) - + switchMap((v) => this.updateAgentContents(this.backendURI)), + catchError((e) => { return of([])})) } - public logout(): Observable<any> { - sessionStorage.removeItem('keys'); - return this.killAgent(); - } - public login(authservice: SshAuthzServer) { - let redirect_uri = window.location.origin+this.locationStrategy.getBaseHref()+"sshauthz_callback"; - if (redirect_uri.includes("file:///")) { - redirect_uri = "http://localhost:4200/sshauthz_callback"; - } - let nonce=Math.random().toString(36).substring(2, 15) - - sessionStorage.setItem('authservice', JSON.stringify([authservice,nonce])); - sessionStorage.setItem('path', '/launch'); - if (authservice.scope == null) { - window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id); - } else { - window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id+"&scope="+authservice.scope); - } - } - - private httperror(error: any) { - this.notifications.notify('There was an error logging in or generating crypto tokens'); - console.error(error); - } private handleError<T> (result?: T) { return (error: any): Observable<T> => { @@ -366,7 +332,10 @@ public getKeys(id?: Identity): KeyCert[] { }; } - initKeygenPipelines() { + private initKeygenPipelines() { + + // every time the fragment changes, extract the token + // every time the token changes, const token$: Observable<AuthToken> = this.fragment$.pipe( filter((v) => v !== null), @@ -397,8 +366,9 @@ public getKeys(id?: Identity): KeyCert[] { let agent$ = combineLatest([keycert$.pipe(filter((v) => v !== null)),apiserver$]).pipe( switchMap(([keycert,apiserver]) => this.addCert(keycert,apiserver)), + tap((v) => this.refresh()), tap((v)=>console.log('posted cert to agent',v)), - switchMap((_) => this.updateAgentContents()), + //switchMap((_) => this.updateAgentContents()), //switchMap((_) => this.loggedInAuthZ), switchMap((_) => of([null])), ); @@ -412,7 +382,7 @@ public getKeys(id?: Identity): KeyCert[] { } ) } - extractToken(frag: string) { + private extractToken(frag: string) { if (frag === undefined || frag == null) { return; } @@ -437,13 +407,13 @@ public getKeys(id?: Identity): KeyCert[] { } - logout_sshauthz(sshauthzserver) { + private logout_sshauthz(sshauthzserver) { if (sshauthzserver !== undefined && sshauthzserver.logout !== null) { window.open(sshauthzserver.logout); } } - getCert(token: AuthToken, key: any, apiserver: APIServer): Observable<any> { + private getCert(token: AuthToken, key: any, apiserver: APIServer): Observable<any> { let headers = new HttpHeaders({'Authorization':'Bearer '+token.token}); let options = { headers: headers, withCredentials: false}; var now = new Date() @@ -458,26 +428,32 @@ public getKeys(id?: Identity): KeyCert[] { ) } - addCert(kclist: any, apiserver: APIServer): Observable<any> { + private 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); + this.storeKey(keyCert); + let data = {'key': keyCert.key, 'cert': keyCert.cert}; + if (this.ipcService.useIpc) { + return this.ipcService.addCert(data); + } else { + + if (this.sessionToken === null) { + throwError('Session token is null how is this possible') + } + + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.sessionToken}` + }); + let options = { headers: headers, withCredentials: true}; + let data = {'key': keyCert.key, 'cert': keyCert.cert}; + return this.http.post<any>(apiserver.tes+'/sshagent',data,options); + + } } - public sshAdd(keyCert: KeyCert, apiserver): Observable<any> { - this.storeKey(keyCert); - let headers = new HttpHeaders(); - let options = { headers: headers, withCredentials: true}; - let data = {'key': keyCert.key, 'cert': keyCert.cert}; - if (this.ipcService.useIpc) { - return this.ipcService.addCert(data); - } else { - return this.http.post<any>(apiserver.tes+'/sshagent',data,options); - } - } - storeKey(keyCert: KeyCert) { + private storeKey(keyCert: KeyCert) { var keys: KeyCert[] = []; try{ keys = JSON.parse(sessionStorage.getItem('keys')); @@ -488,6 +464,10 @@ public getKeys(id?: Identity): KeyCert[] { keys = []; } keys.push(keyCert); + if (keys.length > 10) { + console.error('keys is growing too fast') + keys = keys.slice(-9) + } sessionStorage.setItem('keys',JSON.stringify(keys)) } diff --git a/src/app/computesites.service.ts b/src/app/computesites.service.ts index 10d54677354616aadc3ac809423274d50c8af677..e457387890465d5d31ac0700f5b0e1537b607d28 100644 --- a/src/app/computesites.service.ts +++ b/src/app/computesites.service.ts @@ -25,7 +25,7 @@ export class ComputesitesService { this.computesites = new BehaviorSubject<Computesite[]>([]); this.getComputeSites(); - this.identities$ = combineLatest([this.authorisationService.agentContents$,this.computesites.pipe(filter((v) => v.length !== 0))]) + this.identities$ = combineLatest([this.authorisationService.availableKeys$,this.computesites.pipe(filter((v) => v.length !== 0))]) .pipe( switchMap((v) => this.combine_cert_site(v[0],v[1])) ) diff --git a/src/app/jobs.service.ts b/src/app/jobs.service.ts index 680cf8ee0ec9761d90ae4ee189160bf1f162611c..573087943aa12fa1ee1e302eddb38febb778cc5f 100644 --- a/src/app/jobs.service.ts +++ b/src/app/jobs.service.ts @@ -75,13 +75,9 @@ export class JobsService { if (identity.expiry < Date.now() && (error.hasOwnProperty("status") && error.status == 401)) { console.error('login expired while refreshing job list expiry',identity.expiry,'current time',Date.now() ); this.cancelRequests$.next(true); - this.notifications.notify("Your login has expired. Please log in again", () => { this.authService.refresh$.next(true) } ); + this.notifications.notify("Your login has expired. Please log in again", () => { this.authService.refresh() } ); return throwError(error); } - if (identity.expiry > Date.now() && error.hasOwnProperty("status") && error.status == 401) { - this.authService.reAdd$.next(identity); - return of([]); - } if (error.hasOwnProperty("status") && error.status == 500) { this.notifications.notify("The Strudel2 API server had an error.\n Please report this via the contact us link.\nThe error message was"+emsg); diff --git a/src/app/launcher/launcher.component.ts b/src/app/launcher/launcher.component.ts index c6adc28aa20d4d3fa6427bc00d61b9a50925a1c8..a1e469c4a425a5d5e7854282ccd7bf205228f5b6 100644 --- a/src/app/launcher/launcher.component.ts +++ b/src/app/launcher/launcher.component.ts @@ -72,7 +72,7 @@ export class LauncherComponent implements OnInit { ngOnInit() { this.pipelines(); - this.sub = this.authService.refresh$.subscribe( () => {this.destroy$.next(true); this.pipelines()}) + this.sub = this.authService.loggedInAuthZ$.subscribe( () => {this.destroy$.next(true); this.pipelines()}) } pipelines() { diff --git a/src/app/launchform/launchform.component.ts b/src/app/launchform/launchform.component.ts index 2eb944497a0886d625bbf8e9590c80747981bea4..39b9a892f56409b870281e56446b3a62234798ba 100644 --- a/src/app/launchform/launchform.component.ts +++ b/src/app/launchform/launchform.component.ts @@ -1,13 +1,14 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { angularMaterialRenderers } from '@jsonforms/angular-material'; import { and, createAjv, isControl, optionIs, rankWith, schemaTypeIs, scopeEndsWith, Tester } from '@jsonforms/core'; -import { Observable } from 'rxjs'; -import { tap} from 'rxjs/operators'; +import { Observable, of, throwError } from 'rxjs'; +import { catchError, tap} from 'rxjs/operators'; import { TesService } from '../tes.service'; import { Strudelapp, AppAction } from '../strudelapp'; import { Job } from '../job'; import { BatchInterface } from '../batchinterface'; import { Identity } from '../identity'; +import { HttpErrorResponse } from '@angular/common/http'; @@ -47,9 +48,33 @@ export class LaunchformComponent { } + private handleLaunchFormError(e: HttpErrorResponse): Observable<any> { + const errordataschema: any = { + "properties": { + "msg": { + "default": "Something went wrong", + "title": "Msg", + "type": "string" + } + }, + "title": "ErrorMsg", + "type": "object" + }; + const erroruischema: any = { + "type": "Control", + "scope": "#" + }; + if (e.status == 500) { + return of({'schema': errordataschema, 'uischema': erroruischema, 'data': {'msg': e.message}}) + } + throwError(e) + } + ngOnInit(): void { + this.schemaObs$ = this.tes.runCommand(this.identity, this.app.submitcmd, {'input': ""}).pipe( tap((v) => console.log(v)), + catchError((e) => this.handleLaunchFormError(e)), tap((v) => { this.schema = v.schema ; this.uischema = v.uischema; this.data = v.data }), tap(() => {console.log('data for the form is set to ',this.data)}) ) diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index 7b00d4a37662faf9350c900f0c7df2eb1cbd863f..ee1f9f5d601d4db76bd1c617202ddbed87d35e9c 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -18,7 +18,7 @@ import { IpcService } from '../ipc.service'; import {DomSanitizer} from '@angular/platform-browser'; import {PipeTransform, Pipe} from "@angular/core"; -import { ThisReceiver } from '@angular/compiler'; +import { LocationStrategy } from '@angular/common'; @Pipe({ name: 'safeHtml'}) export class SafeHtmlPipe implements PipeTransform { @@ -52,6 +52,7 @@ export class LoginComponent implements OnInit { public overlayContainer: OverlayContainer, private router: Router, private ipcService: IpcService, + private locationStrategy: LocationStrategy, ) { @@ -90,9 +91,28 @@ export class LoginComponent implements OnInit { } */ - login () { +// login () { //localStorage.setItem('strudel-navlaunch','true'); - this.authService.login(this.selected); + //this.authService.login(this.selected); +// } + + public login() { + let authservice = this.selected; + // Initiate Login + let redirect_uri = window.location.origin+this.locationStrategy.getBaseHref()+"sshauthz_callback"; + if (redirect_uri.includes("file:///")) { + redirect_uri = "http://localhost:4200/sshauthz_callback"; + } + let nonce=Math.random().toString(36).substring(2, 15) + + sessionStorage.setItem('authservice', JSON.stringify([authservice,nonce])); + sessionStorage.setItem('path', '/launch'); + if (authservice.scope == null) { + window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id); + } else { + window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id+"&scope="+authservice.scope); + } } + } diff --git a/src/app/logout/logout.component.ts b/src/app/logout/logout.component.ts index d9e2f54be9191c28c092239edf29412c1572615b..80d6476694fdf2dac51f1eb8df0e84161adf22cf 100644 --- a/src/app/logout/logout.component.ts +++ b/src/app/logout/logout.component.ts @@ -2,6 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { Router, NavigationStart } from '@angular/router'; import { AuthorisationService } from '../authorisation.service'; +import { catchError } from 'rxjs/operators'; +import { of } from 'rxjs'; @Component({ selector: 'app-logout', @@ -20,8 +22,8 @@ export class LogoutComponent implements OnInit { } logout() { - this.authService.logout().subscribe(() => this.router.navigate(['/login'])); - //this.router.navigate(['/login']); + this.authService.logout(); + this.authService.loggedInAuthZ$.subscribe(() => this.router.navigate(['/login'])); } } diff --git a/src/app/noaccount/noaccount.component.ts b/src/app/noaccount/noaccount.component.ts index 02be12ec12d65f8912972448517b8d1e312d98d7..3f534508056ad01b13af60355fafa6c5bde6ea4f 100644 --- a/src/app/noaccount/noaccount.component.ts +++ b/src/app/noaccount/noaccount.component.ts @@ -21,7 +21,7 @@ export class NoaccountComponent implements OnInit { } ngOnInit() { - this.subscription = combineLatest([ this.route.paramMap.pipe(filter((v) => v !== null)), this.authService.sshAuthzServers$.pipe(filter((v) => v !== null))]) + this.subscription = combineLatest([ this.route.paramMap.pipe(filter((v) => v !== null)), this.authService.loggedOutAuthZ$.pipe(filter((v) => v !== null))]) .subscribe(([m,s]) => this.setSite(m,s)); } diff --git a/src/app/shareconnect/shareconnect.component.css b/src/app/shareconnect/shareconnect.component.css deleted file mode 100644 index 683ae23faeb400f1a70bacba6e68504c296bc4ef..0000000000000000000000000000000000000000 --- a/src/app/shareconnect/shareconnect.component.css +++ /dev/null @@ -1,3 +0,0 @@ -:host { - height: 100%; -} diff --git a/src/app/shareconnect/shareconnect.component.html b/src/app/shareconnect/shareconnect.component.html deleted file mode 100644 index c80d908b49e1ca8b4d7f01fb4d1b8fdff2bfadb3..0000000000000000000000000000000000000000 --- a/src/app/shareconnect/shareconnect.component.html +++ /dev/null @@ -1,38 +0,0 @@ -<div fxFlex style="flex: 1 1 0%; box-sizing: border-box"> -<div fxLayout="column" fxLayoutAlign="space-around stretch" style="height: 100%; width: 100%" > - <mat-toolbar color="primary"> - <mat-toolbar-row> - <!--<button mat-icon-button (click)=idSideNav.toggle()><mat-icon>menu</mat-icon></button>--> - - <span>Strudel v2.0</span> - <span class="fill-horizontal-space"></span> - </mat-toolbar-row> - </mat-toolbar> - -<mat-sidenav-container #idSideNav autosize style="height: 100%; width: 100%"> - <mat-sidenav #idSideNav mode="side" opened> - <app-sv2-side-nav></app-sv2-side-nav> - </mat-sidenav> - - <div fxLayout="column" fxLayoutAlign="none" style="height: 100%; width: 100%"> - - <!-- <div style="height: 600px"></div> --> - <form> - <mat-form-field style="width: 100%; height: 100%"> - <textarea fxFlex matInput placeholder="Paste the data sent via email" style="width: 100%; height: 100%" [(ngModel)]="data" name="data"></textarea> - </mat-form-field> - </form> - - <div style="width: 100%" fxLayout="row" fxLayoutAlign="none"> - <div fxFlex></div> - <button mat-button (click)="onSubmit()">Connect</button> - <div fxFlex></div> - </div> - </div> - - - - <!-- <mat-card style="width: 100%; box-sizing: border-box">Transfer tasks</mat-card> --> -</mat-sidenav-container> -</div> -</div> diff --git a/src/app/shareconnect/shareconnect.component.spec.ts b/src/app/shareconnect/shareconnect.component.spec.ts deleted file mode 100644 index faea16e7afa4891ff43e14a3fe573f72afd4d2d4..0000000000000000000000000000000000000000 --- a/src/app/shareconnect/shareconnect.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { ShareconnectComponent } from './shareconnect.component'; - -describe('ShareconnectComponent', () => { - let component: ShareconnectComponent; - let fixture: ComponentFixture<ShareconnectComponent>; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ ShareconnectComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ShareconnectComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/shareconnect/shareconnect.component.ts b/src/app/shareconnect/shareconnect.component.ts deleted file mode 100644 index f1bea48d6e5b436be6a21d1cd35f9c514d26ce16..0000000000000000000000000000000000000000 --- a/src/app/shareconnect/shareconnect.component.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Strudelapp } from '../strudelapp'; -import { TesService } from '../tes.service'; -import { AuthorisationService } from '../authorisation.service'; -import { Computesite } from '../computesite'; -import { ComputesitesService } from '../computesites.service'; -import { Router } from '@angular/router'; -import { Job } from '../job'; -import { Subscription } from 'rxjs'; -import {Identity, KeyCert} from '../identity'; - - -@Component({ - selector: 'app-shareconnect', - templateUrl: './shareconnect.component.html', - styleUrls: ['./shareconnect.component.css'] -}) -export class ShareconnectComponent implements OnInit { - - data: string = ""; - invalid: boolean = false; - subscription: Subscription; - constructor(private computesitesService: ComputesitesService, - private tesService: TesService, - private authService: AuthorisationService, - private router: Router) { } - - ngOnInit() { - setTimeout( () => this.authService.updateAgentContents() ) - } - - connect(jobdata: Job) { - //this.tesService.connect(jobdata); - } - - onSubmit() { - var dataobject: Object; - var jobdata: Job; - var site: Computesite; - var id: Identity; - try { - dataobject = JSON.parse(this.data); - jobdata = <Job>dataobject['job']; - site = <Computesite>dataobject['site']; - id = <Identity>dataobject['identity']; - id.site = site; - jobdata.identity = new Identity(id.username, site, id.expiry); - - let keycert = dataobject['keycert']; - if (keycert !== null) { - this.subscription = this.authService.agentContents$.subscribe(() => this.connect(jobdata)) - //this.authService.sshAdd(keycert); - } else { - this.connect(jobdata); - } - this.invalid=false; - } catch { - if (this.subscription !== undefined) { - this.subscription.unsubscribe(); - } - this.invalid=true; - } - } - -} diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.css b/src/app/sv2-side-nav/sv2-side-nav.component.css deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.html b/src/app/sv2-side-nav/sv2-side-nav.component.html deleted file mode 100644 index 3fd6edc304c3496af54a5b87124ee1f4318c2b0a..0000000000000000000000000000000000000000 --- a/src/app/sv2-side-nav/sv2-side-nav.component.html +++ /dev/null @@ -1,19 +0,0 @@ -<!-- <mat-accordion> --> -<mat-expansion-panel> - <mat-expansion-panel-header> - <mat-panel-title> - More Services - </mat-panel-title> - </mat-expansion-panel-header> - <div *ngFor="let sshauthzserver of (authService.loggedInAuthZ$ | async)" style="width: 100%"> - <button mat-button (click) =logout()>Logout {{ sshauthzserver.name }}</button> - </div> - <div *ngFor="let sshauthzserver of (authService.loggedOutAuthZ$ | async)" style="width: 100%"> - <button mat-button (click)=login(sshauthzserver) fxFlex style="text-align: left"> Run on {{ sshauthzserver.name }}</button> - <button mat-icon-button *ngIf="sshauthzserver.userdefined === true"><mat-icon>remove</mat-icon></button> - </div> - <div style="width: 100%"> - <button mat-button (click) ="authService.updateAgentContents()"> Refresh </button> - </div> -</mat-expansion-panel> -<!-- </mat-accordion> --> diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.spec.ts b/src/app/sv2-side-nav/sv2-side-nav.component.spec.ts deleted file mode 100644 index 97de6bd25450a1d151558df33ed8fc869c823a4c..0000000000000000000000000000000000000000 --- a/src/app/sv2-side-nav/sv2-side-nav.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { Sv2SideNavComponent } from './sv2-side-nav.component'; - -describe('Sv2SideNavComponent', () => { - let component: Sv2SideNavComponent; - let fixture: ComponentFixture<Sv2SideNavComponent>; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ Sv2SideNavComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(Sv2SideNavComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/sv2-side-nav/sv2-side-nav.component.ts b/src/app/sv2-side-nav/sv2-side-nav.component.ts deleted file mode 100644 index 212c88cbddffc1a2d828d36852ad146c38b1ee07..0000000000000000000000000000000000000000 --- a/src/app/sv2-side-nav/sv2-side-nav.component.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { MatDialog, MatDialogRef, MAT_DIALOG_DATA, MatDialogModule, MatDialogConfig } from '@angular/material/dialog'; - -import { AuthorisationService } from '../authorisation.service'; -import { LogoutdialogComponent } from '../logoutdialog/logoutdialog.component'; - - -@Component({ - selector: 'app-sv2-side-nav', - templateUrl: './sv2-side-nav.component.html', - styleUrls: ['./sv2-side-nav.component.css'] -}) -export class Sv2SideNavComponent implements OnInit { - - constructor(public authService: AuthorisationService, public dialog: MatDialog,) { } - - ngOnInit() { - } - logout() { - let dialogRef = this.dialog.open(LogoutdialogComponent, { - width: '250px', - height: '400px', - }); - } - - login (sshauthzserver) { - this.authService.login(sshauthzserver); - } - -} diff --git a/src/app/tes.service.ts b/src/app/tes.service.ts index 16b76687ed0bccb0246ce47433c82e776117322b..9920712cf4d37e7971699ddc984bc4781daa2a5b 100644 --- a/src/app/tes.service.ts +++ b/src/app/tes.service.ts @@ -76,7 +76,7 @@ public openWindow$: Subject<any>; private getUserHealthError(error: any, identity: Identity) { identity.accountalerts.next([]); if (identity.expiry < Date.now() || (error.hasOwnProperty("status") && error.status == 401)) { - this.notifications.notify("Your login to "+identity.site.name+ " has expired. Please log in again", () => { this.authorisationService.refresh$.next(true); }); + this.notifications.notify("Your login to "+identity.site.name+ " has expired. Please log in again", () => { this.authorisationService.refresh(); }); this.cancelHealthRequests(); return; @@ -87,7 +87,10 @@ public openWindow$: Subject<any>; getUserHealth(identity: Identity) { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; let params = new URLSearchParams(); if (identity === undefined || identity === null) { @@ -109,7 +112,10 @@ getUserHealth(identity: Identity) { } getUserHealthObservable(identity: Identity) { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; let params = new URLSearchParams(); if (identity === undefined || identity === null) { @@ -154,7 +160,10 @@ public getHealthAlertsObservable(identity: Identity) { } public getCachetIncidentsObservable(identity: Identity) { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: false}; let params = new URLSearchParams(); if (identity.site.cacheturis === undefined || identity.site.cacheturis.length == 0) { @@ -209,7 +218,7 @@ public addUserHealth(identity,resp) { console.error(error); if (error.status == 401) { console.error('getCacheIncidentsError'); - this.notifications.notify("Your login has expired. Please log in again", () => { this.authorisationService.refresh$.next(true); } ); + this.notifications.notify("Your login has expired. Please log in again", () => { this.authorisationService.refresh(); } ); return } if (error.status == 400) { @@ -222,7 +231,9 @@ public addUserHealth(identity,resp) { submissionError(identity: Identity, error: any) { if (identity.expiry < Date.now()) { console.error('submissionError'); - this.notifications.notify("Your login has expired. Please log in again", () => { this.authorisationService.updateAgentContents().subscribe( () => {return} ) } ); + console.log(error); + //this.notifications.notify("Your login has expired. Please log in again", () => { this.authorisationService.updateAgentContents().subscribe( () => {return} ) } ); + this.notifications.notify("Your login has expired. Please log in again", () => this.authorisationService.refresh()) return; } if (error.status != 0) { @@ -249,7 +260,10 @@ public addUserHealth(identity,resp) { } submit(app: Strudelapp, identity: Identity, batchinterface: BatchInterface, ftidentities?: Identity[], appparams?: any) { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; this.notifications.notify('Submitting job'); //let keys = JSON.stringify(this.authorisationService.getKeys()); @@ -272,7 +286,10 @@ public addUserHealth(identity,resp) { cancel(job: Job) { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; let cmd = job.identity.site.cancelcmd.replace("{jobid}",job.jobid); @@ -299,25 +316,31 @@ public addUserHealth(identity,resp) { let username = job.identity.username; let loginhost = job.identity.site.host; let batchhost = job.batch_host; + let port = appinst.port; let params = new URLSearchParams; - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); job.connectionState = 2; + let body = { + 'user': username, + 'host': batchhost, + 'bastion': loginhost, + 'appinst': appinst, + 'port': port, + 'appname': job.appname + } if ((action.notunnel != undefined) && (action.notunnel == true)) { return of('') } let options = { headers: headers, withCredentials: true}; - let paramstr = params.toString(); if (this.ipcService.useIpc) { - let body = { - 'user': username, - 'host': batchhost, - 'bastion': loginhost, - 'appinst': appinst - } + return this.ipcService.createTunnel(body) } else { - return this.http.post<string>(apiserver.tes+'/createtunnel/'+username+'/'+loginhost+'/'+batchhost+'?'+paramstr, appinst, options) + return this.http.post<string>(apiserver.tes+'/createtunnel', body, options) } } @@ -339,31 +362,52 @@ public addUserHealth(identity,resp) { return of(url); } - public getAppUrl(job: Job, appinst: any, action: AppAction, apiserver: APIServer): Observable<any> { - let params = new URLSearchParams; - let headers = new HttpHeaders(); - let options = { headers: headers, withCredentials: true}; - console.log('in getAppUrl'); - if (this.ipcService.useIpc) { - console.log('try local'); - return this.getAppUrlLocal(job, appinst, action).pipe( - tap((v)=>console.log('try to open url',v)) - ) - } - let pseudoapp = {'client':{'redir':action.client.redir,'cmd':action.client.cmd}}; - params.set('app',JSON.stringify(pseudoapp)); - params.set('appinst',JSON.stringify(appinst)); - let paramstr = params.toString(); - job.connectionState = 3; - if (action.client.cmd !== undefined && action.client.cmd !== null) { - return this.http.get<string>(apiserver.tes+'/applaunch?'+paramstr,options) - } - console.log('getting appurl from the api server'); - return this.http.get<string>(apiserver.tes+'/appurl?'+paramstr,options) + private getAppUrl(job: Job, appinst: any, action: AppAction, apiserver: APIServer): string { + + console.log('in app urlLocal',appinst); + let url = apiserver.tws+'/'+action.client.redir; + for (let key in appinst) { + if (appinst.hasOwnProperty(key)) { + let value = appinst[key] + url = url.replace("{"+key+"}",value) + } + } + console.log('appUrlLocal will be',url); + return url; + } +// public getAppUrl(job: Job, appinst: any, action: AppAction, apiserver: APIServer): Observable<any> { +// let params = new URLSearchParams; +// let headers = new HttpHeaders({ +// 'Authorization': `Bearer ${this.authorisationService.sessionToken}` +// }); +// //let headers = new HttpHeaders(); +// let options = { headers: headers, withCredentials: true}; +// console.log('in getAppUrl'); +// if (this.ipcService.useIpc) { +// console.log('try local'); +// return this.getAppUrlLocal(job, appinst, action).pipe( +// tap((v)=>console.log('try to open url',v)) +// ) +// } +// let pseudoapp = {'client':{'redir':action.client.redir,'cmd':action.client.cmd}}; +// params.set('app',JSON.stringify(pseudoapp)); +// params.set('appinst',JSON.stringify(appinst)); +// let paramstr = params.toString(); +// job.connectionState = 3; +// if (action.client.cmd !== undefined && action.client.cmd !== null) { +// return this.http.get<string>(apiserver.tes+'/applaunch?'+paramstr,options) +// } +// console.log('getting appurl from the api server'); +// return this.http.get<string>(apiserver.tes+'/appurl?'+paramstr,options) +// } + public contactUs(form: any) { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; return this.backendSelectionService.apiserver.pipe( filter((v) => v !== undefined && v !== null), @@ -403,7 +447,9 @@ public addUserHealth(identity,resp) { public connect(job: Job, action: AppAction, appinst?: any) { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); let options = { headers: headers, withCredentials: true}; job.connectionState=1 @@ -426,15 +472,19 @@ public addUserHealth(identity,resp) { catchError(err => this.handleTunnelError(job,action,err)), map((v) => { - appinst['localport'] = v['localport']; + if (v !== null && v.hasOwnProperty('localport')) { + appinst['localport'] = v['localport']; + } return [apiserver, appinst]; })) } ), tap((_) => job.connectionState=3), - switchMap(([apiserver,appinst]) => - { return this.getAppUrl(job,appinst,action,<APIServer>apiserver) - .pipe(map((url) => [apiserver, appinst, url])) - }), + + map( ([apiserver, appinst]) => { + let url = this.getAppUrl(job,appinst,action,<APIServer>apiserver); + return [apiserver, appinst, url] + }), + tap((_) => job.connectionState=4), ).subscribe(([apiserver,appinst,url]) => { if (url !== null) { this.openWindow$.next( {'job':job, @@ -448,7 +498,10 @@ public addUserHealth(identity,resp) { } public runCommand(identity: Identity, command: string, optional?: any): Observable<any> { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; if (identity === undefined || identity === null) { return @@ -481,7 +534,10 @@ public addUserHealth(identity,resp) { public postMkDir(id: Identity, path: string, name: string ) { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; let params = new URLSearchParams; params.set('identity',JSON.stringify(id)); @@ -491,7 +547,10 @@ public postMkDir(id: Identity, path: string, name: string ) { } public getSftpData(id: Identity, path: string, cd: string ) { - let headers = new HttpHeaders(); + let headers = new HttpHeaders({ + 'Authorization': `Bearer ${this.authorisationService.sessionToken}` + }); + //let headers = new HttpHeaders(); let options = { headers: headers, withCredentials: true}; let params = new URLSearchParams; params.set('identity',JSON.stringify(id)); @@ -559,13 +618,10 @@ private handleGenericError(job: Job, action: AppAction, error: HttpErrorResponse } if (job.identity.expiry < Date.now() && (error.hasOwnProperty("status") && error.status == 401)) { console.error('handleGenericError'); - this.notifications.notify("Your login has expired. Please log in again", () => { this.authorisationService.refresh$.next(true); } ); + this.notifications.notify("Your login has expired. Please log in again", () => { this.authorisationService.refresh(); } ); return throwError(error); } - if (job.identity.expiry > Date.now() && error.hasOwnProperty("status") && error.status == 401) { - this.authorisationService.reAdd$.next(job.identity); - this.notifications.notify("Your login expired. Logging you back in again automatically. Please rety what you were doing"); - } + if (error.hasOwnProperty("status") && error.status == 500) { this.notifications.notify("The Strudel2 API server had an error.\n Please report this via the contact us link.\nThe error message was"+emsg); diff --git a/src/app/transfer/transfer.component.css b/src/app/transfer/transfer.component.css deleted file mode 100644 index 683ae23faeb400f1a70bacba6e68504c296bc4ef..0000000000000000000000000000000000000000 --- a/src/app/transfer/transfer.component.css +++ /dev/null @@ -1,3 +0,0 @@ -:host { - height: 100%; -} diff --git a/src/app/transfer/transfer.component.html b/src/app/transfer/transfer.component.html deleted file mode 100644 index 5f4163b660c4c5eaf2fb8550425c816366c87cda..0000000000000000000000000000000000000000 --- a/src/app/transfer/transfer.component.html +++ /dev/null @@ -1,48 +0,0 @@ - -<div fxLayout="column" fxLayoutAlign="flex-start stretch" style="height: 100%; width: 100%" > - - <!--<div fxLayout="row" fxLayout="space-around stretch" fxLayoutGap="1%" style="height: 90%">--> - <!--<div fxLayout="row" fxLayout="space-around stretch" fxLayoutGap="1%" style="height: 90%">--> - <div fxLayout="row" fxLayout="space-around stretch" fxLayoutGap="1%" style="height: 75%"> - - <div fxFlex *ngFor="let idx of [0,1]" style="height: 100%;"> - <!-- <div *ngFor="let idx of [0,1]" style="height: 100% " fxFlex>--> - <!--<div fxLayout="column" fxLayoutAlign="none stretch" style="width: 100%; height: 100%" >--> - <!--<div style="width: 100%; height: 100%" >--> - <mat-form-field style="width:100%"> - <mat-select (selectionChange)="setId(idx,$event)" placeholder="Choose a server"> - <mat-option *ngFor="let s of computesitesService.ftidentities | async" [value]="s"> - {{ s.username }}@{{s.site.name }} - </mat-option> - </mat-select> - </mat-form-field> - <span class="mat-body"> {{ path[idx] }}</span> - <div *ngIf="working[idx] | async"> - <mat-spinner></mat-spinner> - </div> - <!--<div *ngIf="!(working[idx] | async)" style="height: 100%; overflow-y: auto">--> - <div *ngIf="!(working[idx] | async)" style="height: 100%"> - <div style="height: 100%; width: 100%; overflow-y: auto"> - <file-explorer [fileElements]="fileElements[idx] | async" - [path]="pathSubject[idx] | async" - (navigatedDown)="navigateToFolder(idx,$event)" - (navigatedUp)="navigateUp(idx,$event)" - [canNavigateUp]="canNavigateUp[idx] | async" - (sendFile)="queueFile(idx,$event)" - (folderAdded)="newDirectory(idx,$event)"> - </file-explorer> - </div> - </div> - <!--</div>--> - </div> - </div> - <div style="height: 25%; overflow-y: auto"> - <h3>Transfer list</h3> - <mat-list> - <div *ngFor="let t of transferlist"> - <mat-list-item> - {{ t.srcpath }} {{ t.dstpath }} - </mat-list-item> - </div> - </mat-list> - </div> -</div> diff --git a/src/app/transfer/transfer.component.spec.ts b/src/app/transfer/transfer.component.spec.ts deleted file mode 100644 index 153d995690cfc97878655298081dc04601a63482..0000000000000000000000000000000000000000 --- a/src/app/transfer/transfer.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; - -import { TransferComponent } from './transfer.component'; - -describe('TransferComponent', () => { - let component: TransferComponent; - let fixture: ComponentFixture<TransferComponent>; - - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - declarations: [ TransferComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(TransferComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/transfer/transfer.component.ts b/src/app/transfer/transfer.component.ts deleted file mode 100644 index bd754650a49fbb82d2ad23cdb31a09de158ba1c3..0000000000000000000000000000000000000000 --- a/src/app/transfer/transfer.component.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; -import { FileElement } from '../file-explorer/model/element'; -import { TesService } from '../tes.service'; -import { ComputesitesService } from '../computesites.service'; -import { AuthorisationService } from '../authorisation.service'; -import { Computesite } from '../computesite'; -import { Identity } from '../identity'; -import { Router } from '@angular/router'; -import {NotificationsService } from '../notifications.service'; - - -export class Transfer { - srcpath: string; - dstpath: string; - name: string; - direction: number; - constructor(srcpath: string, dstpath: string, name: string, direction: number) { - this.srcpath = srcpath; - this.dstpath = dstpath; - this.name = name; - this.direction = direction; - } -} - - - -@Component({ - selector: 'app-transfer', - templateUrl: './transfer.component.html', - styleUrls: ['./transfer.component.css'] -}) -export class TransferComponent implements OnInit { - - fileElements: BehaviorSubject<FileElement[]>[]; - pathSubject: BehaviorSubject<string>[]; - path: string[]; - working: BehaviorSubject<Boolean>[]; - canNavigateUp: BehaviorSubject<Boolean>[]; - id: Identity[]; - site: Computesite[]; - disableSend: Boolean; - redirecturi: string; - appevent: any; - transferlist: Transfer[]; - - - constructor(public computesitesService: ComputesitesService, - private tesService: TesService, - private authService: AuthorisationService, - private router: Router, - private notifications: NotificationsService) { - this.path = ['','']; - this.fileElements = [new BehaviorSubject([]), new BehaviorSubject([])]; - this.working = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)]; - this.canNavigateUp = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)]; - this.pathSubject = [ new BehaviorSubject(""),new BehaviorSubject("")]; - this.id = [null,null]; - this.disableSend = true; - this.transferlist = []; - } - - ngOnInit() { - var fel: FileElement[] = [] - // this.fileElements = [new BehaviorSubject([]), new BehaviorSubject([])]; - // this.working = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)]; - // this.canNavigateUp = [ new BehaviorSubject<Boolean>(false), new BehaviorSubject<Boolean>(false)]; - // this.pathSubject = [ new BehaviorSubject(""),new BehaviorSubject("")]; - // this.id = [null,null]; - // this.disableSend = true; - setTimeout( () => this.authService.updateAgentContents() ) - } - -compareObjects(o1: any, o2: any) { - if(o1.name == o2.name) { - return true; - } else { - return false; - } -} - -processError(idx,error) { - this.working[idx].next(false); - this.notifications.notify(error.error.message); - -} - -newDirectory(idx: number, name: string) { - this.working[idx].next(true); - this.tesService.postMkDir(this.id[idx],this.path[idx],name).subscribe(resp => this.getSftpls(idx,'.'), error => this.processError(idx,error)); -} - -getSftpls(idx,cd) { - this.working[idx].next(true); - this.tesService.getSftpData(this.id[idx],this.path[idx],cd).subscribe(resp => this.updateSftpls(idx,resp), error => this.processError(idx,error)); -} - -makeFileElements(data) { - var rv: FileElement[] = []; - var fe: FileElement; - for (let f of data) { - fe = new FileElement(); - if (f.isdir !== undefined) { - fe.isFolder = f.isdir; - } else { - if (f.mode.indexOf('d')== 0) { - fe.isFolder=true; - } - } - fe.name = f.name; - fe.size = f.size; - fe.online = f.online; - rv.push(fe); - } - return rv; -} - - updateSftpls(idx,resp) { - let files = this.makeFileElements(resp.files); - if (resp.pwd.length > 1) { - this.canNavigateUp[idx].next(true); - } else { - this.canNavigateUp[idx].next(false); - - } - this.path[idx] = resp.pwd; - this.pathSubject[idx].next(this.path[idx]); - this.fileElements[idx].next(files); - this.working[idx].next(false); - - } - - - setId(idx,event) { - this.id[idx] = event.value; - if ((this.id[0] != null) && (this.id[1] != null)) { - this.disableSend = false; - } - this.path[idx]="."; - this.pathSubject[idx].next(this.id[idx].username+"@"+this.id[idx].site.name+":"+this.path[idx]); - this.getSftpls(idx,'.'); - - } - - navLaunch() { - this.router.navigate(['/launch']); - } - - navigateToFolder(idx,element: FileElement) { - this.getSftpls(idx,element.name); - } - - navigateUp(idx: number, event: any) { - this.getSftpls(idx,'..'); - } - - - queueFile(idx: number, element: FileElement) { - let src = idx; - let dest = (idx+1) % 2; - this.transferlist.push(new Transfer(this.path[0],this.path[1],element.name,idx)); - } - - postParams() { - let appparams = JSON.stringify( - {'sites':[this.id[0],this.id[1]], - 'files': this.transferlist }) - window.parent.postMessage({'appData':appparams},'*'); - } - -} diff --git a/src/assets/config/apiservers.json b/src/assets/config/apiservers.json index 0392248fcb6e6eba3af108ee9997ee0b28eea1b1..4d694cdaad4b812ec6c76982d7c5fa5c5ca7834e 100644 --- a/src/assets/config/apiservers.json +++ b/src/assets/config/apiservers.json @@ -16,8 +16,8 @@ }, { "name": "localhost", - "tes": "http://localhost:8080", - "tws": "http://localhost:8090" + "tes": "http://localhost:8000", + "tws": "http://localhost:9000" } ] diff --git a/src/assets/images/mlerp-dragon.png b/src/assets/images/mlerp-dragon.png new file mode 100644 index 0000000000000000000000000000000000000000..44255f075f01b1f5cc2313d64c31717ca9778a6f Binary files /dev/null and b/src/assets/images/mlerp-dragon.png differ diff --git a/src/assets/mlerp-dragon.png b/src/assets/mlerp-dragon.png new file mode 100644 index 0000000000000000000000000000000000000000..44255f075f01b1f5cc2313d64c31717ca9778a6f Binary files /dev/null and b/src/assets/mlerp-dragon.png differ diff --git a/src/deployments/mlerp/assets/images/mlerp-dragon.png b/src/deployments/mlerp/assets/images/mlerp-dragon.png new file mode 100644 index 0000000000000000000000000000000000000000..44255f075f01b1f5cc2313d64c31717ca9778a6f Binary files /dev/null and b/src/deployments/mlerp/assets/images/mlerp-dragon.png differ