Commit 4de33b31 authored by Chris Hines's avatar Chris Hines
Browse files

latest build

parent 82a5b259
Pipeline #6440 failed with stages
import { NgModule } from '@angular/core'; import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { LauncherComponent } from './launcher/launcher.component'; import { LauncherComponent } from './launcher/launcher.component';
import { TokenextractorComponent } from './tokenextractor/tokenextractor.component'; import { KeygenComponent } from './keygen/keygen.component';
import { ConnectingComponent } from './connecting/connecting.component';
// import { TokenextractorComponent } from './tokenextractor/tokenextractor.component';
const routes: Routes = [ const routes: Routes = [
{ path: '', redirectTo: 'launch', pathMatch: 'full'}, { path: '', redirectTo: 'launch', pathMatch: 'full'},
{ path: 'launch', component: LauncherComponent}, { path: 'launch', component: LauncherComponent},
// { path: 'sshauthz_callback', component: TokenextractorComponent} { path: 'finishlaunch', component: LauncherComponent},
{ path: 'sshauthz_callback', component: LauncherComponent} { path: 'cancellaunch', component: LauncherComponent},
{ path: 'sshauthz_callback', component: KeygenComponent},
{ path: 'connecting', component: ConnectingComponent }
// { path: 'sshauthz_callback', component: LauncherComponent}
]; ];
......
...@@ -3,3 +3,8 @@ ...@@ -3,3 +3,8 @@
Every toolbar row uses a flexbox row layout. */ Every toolbar row uses a flexbox row layout. */
flex: 1 1 auto; flex: 1 1 auto;
} }
.fill-horizontal-space {
/* This fills the remaining space, by using flexbox.
Every toolbar row uses a flexbox row layout. */
flex: 0 1 auto;
}
<mat-toolbar color="primary">
<mat-toolbar-row>
<span>{{title}}</span>
<span class="fill-remaining-space"></span>
<span class="fill-remaining-space"></span>
<!-- <app-siteselection></app-siteselection> -->
<!-- <button mat-button *ngIf="authorised" (click)="logout()">Logout</button>
<button mat-button *ngIf="!authorised" (click)="login()">Login</button> -->
</mat-toolbar-row>
</mat-toolbar>
<router-outlet></router-outlet>
<app-teserrors></app-teserrors> <router-outlet class="fill-remaining-space"></router-outlet>
<!-- <app-teserrors></app-teserrors> -->
...@@ -15,20 +15,31 @@ import { MatTableModule } from '@angular/material/table'; ...@@ -15,20 +15,31 @@ import { MatTableModule } from '@angular/material/table';
import { MatCardModule } from '@angular/material'; import { MatCardModule } from '@angular/material';
import { MatToolbarModule } from '@angular/material'; import { MatToolbarModule } from '@angular/material';
import { MatDialogModule, MatDialog } from '@angular/material'; import { MatDialogModule, MatDialog } from '@angular/material';
import { MatSnackBarModule } from "@angular/material";
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ComputesitesService } from './computesites.service'; import { ComputesitesService } from './computesites.service';
import { StrudelappsService } from './strudelapps.service'; import { StrudelappsService } from './strudelapps.service';
import { AuthorisationService } from './authorisation.service';
import { TesService} from './tes.service'; import { TesService} from './tes.service';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { JobComponent } from './job/job.component'; import { JobComponent } from './job/job.component';
import { SiteselectionComponent } from './siteselection/siteselection.component'; // import { SiteselectionComponent } from './siteselection/siteselection.component';
import { TeserrorsComponent } from './teserrors/teserrors.component'; import { TeserrorsComponent } from './teserrors/teserrors.component';
import { AppRoutingModule } from './/app-routing.module'; import { AppRoutingModule } from './/app-routing.module';
import { TokenextractorComponent } from './tokenextractor/tokenextractor.component'; import { TokenextractorComponent } from './tokenextractor/tokenextractor.component';
import { LogindialogComponent } from './logindialog/logindialog.component'; // import { LogindialogComponent } from './logindialog/logindialog.component';
import { LogoutdialogComponent } from './logoutdialog/logoutdialog.component'; import { LogoutdialogComponent } from './logoutdialog/logoutdialog.component';
// import { LaunchdialogComponent } from './launchdialog/launchdialog.component';
import { ModaldialogComponent } from './modaldialog/modaldialog.component';
import { MatSidenavModule, MatExpansionModule, MatIconModule } from '@angular/material';
import { StrudelapplistComponent } from './strudelapplist/strudelapplist.component';
import { KeygenComponent } from './keygen/keygen.component';
import { ConnectingComponent } from './connecting/connecting.component';
...@@ -38,11 +49,15 @@ import { LogoutdialogComponent } from './logoutdialog/logoutdialog.component'; ...@@ -38,11 +49,15 @@ import { LogoutdialogComponent } from './logoutdialog/logoutdialog.component';
LauncherComponent, LauncherComponent,
JoblistComponent, JoblistComponent,
JobComponent, JobComponent,
SiteselectionComponent, // SiteselectionComponent,
TeserrorsComponent, TeserrorsComponent,
TokenextractorComponent, TokenextractorComponent,
LogindialogComponent, LogoutdialogComponent,
LogoutdialogComponent ModaldialogComponent,
// LaunchdialogComponent,
StrudelapplistComponent,
KeygenComponent,
ConnectingComponent
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
...@@ -61,10 +76,14 @@ import { LogoutdialogComponent } from './logoutdialog/logoutdialog.component'; ...@@ -61,10 +76,14 @@ import { LogoutdialogComponent } from './logoutdialog/logoutdialog.component';
FormsModule, FormsModule,
HttpClientModule, HttpClientModule,
AppRoutingModule, AppRoutingModule,
MatSidenavModule,
MatExpansionModule,
MatIconModule,
MatSnackBarModule,
], ],
entryComponents: [ LogindialogComponent, LogoutdialogComponent ], entryComponents: [ ModaldialogComponent, LogoutdialogComponent, ],
providers: [ StrudelappsService, ComputesitesService, TesService, MatDialog], providers: [ StrudelappsService, ComputesitesService, TesService, MatDialog, AuthorisationService],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule { } export class AppModule { }
...@@ -8,16 +8,24 @@ import {LocationStrategy} from '@angular/common'; ...@@ -8,16 +8,24 @@ import {LocationStrategy} from '@angular/common';
// import { keypair } from 'keypair'; // import { keypair } from 'keypair';
import * as keypair from 'keypair'; import * as keypair from 'keypair';
import * as forge from "node-forge"; import * as forge from "node-forge";
import { Identity, AuthToken, KeyCert, AuthService } from './identity'; import { Identity, AuthToken, KeyCert, SshAuthzServer } from './identity';
import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Subject} from 'rxjs/Subject'; import {Subject} from 'rxjs/Subject';
import {TesService} from './tes.service'; import {TesService} from './tes.service';
import { throwError } from 'rxjs';
export class SshauthzServer {}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class AuthorisationService { export class AuthorisationService {
private token: Subject<AuthToken>; // public token: BehaviorSubject<AuthToken>;
public token: AuthToken
public SshAuthzServers: BehaviorSubject<SshAuthzServer[]>;
public readyToNavigate: Subject<Boolean>;
public progress: Subject<string>;
// private keyCert: Subject<KeyCert>; // private keyCert: Subject<KeyCert>;
...@@ -26,10 +34,30 @@ export class AuthorisationService { ...@@ -26,10 +34,30 @@ export class AuthorisationService {
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private tesService: TesService) { private tesService: TesService) {
this.token = new Subject<AuthToken>(); console.log('created AuthorisationService');
this.token.subscribe(token => this.getCert(token)); // this.token = new BehaviorSubject<AuthToken>(new AuthToken('',''));
this.readyToNavigate = new Subject<Boolean>();
this.readyToNavigate.next(false);
this.progress = new Subject<string>();
this.progress.next("");
// this.token.subscribe(token => this.getCert(token));
this.route.fragment.subscribe(frag => this.storeToken(frag)); this.route.fragment.subscribe(frag => this.storeToken(frag));
this.SshAuthzServers = new BehaviorSubject<SshAuthzServer[]>([]);
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));
} }
updateSshAuthzServers(resp) {
this.SshAuthzServers.next(<SshAuthzServer[]>resp);
}
storeToken(frag: string) { storeToken(frag: string) {
if (frag === undefined || frag == null) { if (frag === undefined || frag == null) {
return; return;
...@@ -37,8 +65,8 @@ export class AuthorisationService { ...@@ -37,8 +65,8 @@ export class AuthorisationService {
let tokenmatch = null; let tokenmatch = null;
let statematch = null; let statematch = null;
if (!(frag === undefined) && !(frag == null)) { if (!(frag === undefined) && !(frag == null)) {
tokenmatch = frag.match(/access_token\=([\S\s]*?)[&|$]/); tokenmatch = frag.match(/access_token\=([^&]+)(&|$)/);
statematch = frag.match(/state\=([\S\s]*?)[&|$]/); statematch = frag.match(/state\=([^&]+)(&|$)/);
} }
if (tokenmatch == null || statematch == null) { if (tokenmatch == null || statematch == null) {
return; return;
...@@ -46,7 +74,7 @@ export class AuthorisationService { ...@@ -46,7 +74,7 @@ export class AuthorisationService {
let accesstoken = tokenmatch[1]; let accesstoken = tokenmatch[1];
let state = statematch[1]; let state = statematch[1];
this.router.navigate(['/']); // this.router.navigate(['/']);
//Verify that the state matched the nonce we used when initiating login //Verify that the state matched the nonce we used when initiating login
let tuple = JSON.parse(localStorage.getItem('authservice')); let tuple = JSON.parse(localStorage.getItem('authservice'));
...@@ -54,14 +82,13 @@ export class AuthorisationService { ...@@ -54,14 +82,13 @@ export class AuthorisationService {
return return
} }
this.token.next(new AuthToken(tokenmatch[1],tuple[0])); // this.token.next(new AuthToken(tokenmatch[1],tuple[0]));
this.token = new AuthToken(tokenmatch[1],tuple[0]);
// TODO fire off a query to the auth service to get the associated sites
} }
getCert(token: AuthToken) { public getCert(token: AuthToken) {
console.log('in getCert'); console.log('in getCert');
if (token.token === undefined || token.token === '' || token.token == null) { if (token.token === undefined || token.token === '' || token.token == null) {
console.log('no authtoken available, we wont be able to generate a cert'); console.log('no authtoken available, we wont be able to generate a cert');
...@@ -80,46 +107,65 @@ export class AuthorisationService { ...@@ -80,46 +107,65 @@ export class AuthorisationService {
let headers = new HttpHeaders(); let headers = new HttpHeaders();
let options = { headers: headers, withCredentials: true}; let options = { headers: headers, withCredentials: true};
let data = {'token': token.token, 'pubkey': sshpub, 'signing_url': token.authservice.sign}; let data = {'token': token.token, 'pubkey': sshpub, 'signing_url': token.sshauthzservice.sign};
console.log('posting to getcert',this.tesService.Base); console.log('posting to getcert',this.tesService.Base);
this.http.post<any>(this.tesService.Base+'/getcert',data, options) this.http.post<any>(this.tesService.Base+'/getcert',data, options)
.pipe(catchError(this.handleError('getCert',[]))) .pipe(catchError(this.handleError('getCert',[])))
.subscribe(resp => this.makeKeyCert(newkeypair.private, resp, token.authservice)) .subscribe(resp => this.makeKeyCert(newkeypair.private, resp, token.sshauthzservice),
error => this.httperrorLogout(error,token.sshauthzservice));
console.log('getcert complete'); console.log('getcert complete');
} }
makeKeyCert(key: string, resp, authservice: AuthService) {
makeKeyCert(key: string, resp, sshauthzservice: SshAuthzServer) {
let keyCert = new KeyCert() let keyCert = new KeyCert()
keyCert.key = key; keyCert.key = key;
keyCert.cert = resp['cert']; keyCert.cert = resp['cert'];
keyCert.authservice = authservice;
console.log('updating keycert',keyCert); console.log('updating keycert',keyCert);
this.tesService.keyCert.next(keyCert); this.tesService.sshAdd(keyCert);
// 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);
}
this.readyToNavigate.next(true);
} }
public login() { public login(authservice: SshAuthzServer) {
let redirect_uri = window.location.origin+this.locationStrategy.getBaseHref()+"sshauthz_callback"; let redirect_uri = window.location.origin+this.locationStrategy.getBaseHref()+"sshauthz_callback";
let nonce="asdfzxcv"; let nonce="asdfzxcv";
let authservice = new AuthService();
authservice.base = "https://autht.massive.org.au/hpcid/";
authservice.authorise = authservice.base + 'oauth/authorize';
authservice.sign = authservice.base + 'api/v1/sign_key';
authservice.client_id = "86c06039-e589-4b39-9d1f-9eca431be18f";
localStorage.setItem('authservice', JSON.stringify([authservice,nonce])); localStorage.setItem('authservice', JSON.stringify([authservice,nonce]));
window.location.assign(authservice.authorise+"?response_type=token&redirect_uri="+redirect_uri+"&state="+nonce+"&client_id="+authservice.client_id); 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) { private httperror(error: any) {
this.tesService.statusMsg.next(error);
console.log(error); console.log(error);
} }
private httperrorLogout(error: any,sshauthzservice: SshAuthzServer) {
this.tesService.statusMsg.next(error);
console.log(error);
if (!(sshauthzservice.logout === null)) {
window.open(sshauthzservice.logout);
}
}
private handleError<T> (operation = 'operation', result?: T) { private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => { return (error: any): Observable<T> => {
console.log('in handle error',operation); console.log('in handle error',operation);
console.log(error.status); console.log(error.status);
console.error(error); console.error(error);
return of(result as T); if (error.status == 500) {
return throwError("The authorisation server encountered and error. Please try again in a few minutes")
}
return throwError(error.message);
// return of(result as T);
}; };
} }
} }
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
export class Computesite { export class Computesite {
url: string; // The URL runs a web service to help construct sbatch commands url: string; // The URL runs a web service to help construct sbatch commands
// Infact the URL should return the entire interface for // Infact the URL should return the entire interface for
...@@ -7,7 +9,9 @@ export class Computesite { ...@@ -7,7 +9,9 @@ export class Computesite {
cafingerprint: string; // Certificates contain a CA fingerprint. We use this cafingerprint: string; // Certificates contain a CA fingerprint. We use this
// to figure out which compute site a certificate is valid // to figure out which compute site a certificate is valid
// for // for
appCatalog: Strudelapp[]; appCatalog: BehaviorSubject<Strudelapp[]>;
appCatalogUri: string;
} }
export class Strudelapp { export class Strudelapp {
......
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Computesite } from './computesite'; import { Computesite, Strudelapp } from './computesite';
import { COMPUTESITES } from './mock-compute-site'; import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { catchError, map, tap } from 'rxjs/operators';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class ComputesitesService { export class ComputesitesService {
public computesites: BehaviorSubject<Computesite[]>;
constructor(private http: HttpClient,) {
this.computesites = new BehaviorSubject<Computesite[]>([]);
this.computesites.subscribe(computesites => this.getStrudelApps(computesites))
this.getComputeSites();
}
constructor() { } getStrudelApps(computesites: Computesite[]) {
for (let s of computesites) {
let headers = new HttpHeaders();
let options = { headers: headers, withCredentials: false};
this.http.get<Strudelapp[]>(s.appCatalogUri,options)
.pipe(catchError(this.handleError('getStrudelApps')))
.subscribe(resp => this.updateStrudelApps(s.appCatalog,resp));
}
getComputeSites(): Computesite[] {
return COMPUTESITES;
} }
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error);
return of(result as T);
};
}
updateStrudelApps(appCatalog: BehaviorSubject<Strudelapp[]>,apps) {
appCatalog.next(<Strudelapp[]>apps);
}
getComputeSites() {
let headers = new HttpHeaders();
let options = { headers: headers, withCredentials: false};
this.http.get<Strudelapp[]>('./assets/config/computesites.json',options)
.pipe(catchError(this.handleError('getComputeSites')))
.subscribe(resp => this.updateComputeSites(resp));
}
updateComputeSites(resp) {
var computesites: Computesite[] = []
for (let cs of resp) {
let computesite = <Computesite>cs;
computesite.appCatalog = new BehaviorSubject<Strudelapp[]>([])
computesites.push(computesite);
}
this.computesites.next(computesites);
}
returnComputeSites(): Computesite[] {
return this.computesites.value;
}
} }
...@@ -4,37 +4,52 @@ import {Computesite} from './computesite'; ...@@ -4,37 +4,52 @@ import {Computesite} from './computesite';
export class Identity { export class Identity {
username: string; username: string;
site: Computesite; site: Computesite;
authservice: AuthService; authservice: SshAuthzServer;
constructor( username: string, site: Computesite) { constructor( username: string, site: Computesite) {
this.username = username; this.username = username;
this.site = site; this.site = site;
} }
copy_skip_catalog(): Identity {
let id = new Identity(null,null);
id.username = this.username;
id.site = new Computesite();
id.site.url = this.site.url;
id.site.host = this.site.host;
id.site.name = this.site.name;
id.site.cafingerprint = this.site.cafingerprint;
return id;
}
displayName(): string { displayName(): string {
return this.username+'@'+this.site.name; return this.username+'@'+this.site.name;
} }
repr(): string { repr(): string {
return JSON.stringify([this.username,this.site.cafingerprint,this.site.host]); return JSON.stringify([this.username,this.site.cafingerprint,this.site.host,this.site.url]);
} }
} }
export class AuthToken { export class AuthToken {
token: string; token: string;
authservice: AuthService; sshauthzservice: SshAuthzServer;
constructor( token: string, authservice: AuthService ) { constructor( token: string, sshauthzservice: SshAuthzServer ) {
this.token = token; this.token = token;
this.authservice = authservice; this.sshauthzservice = sshauthzservice;
} }
} }
export class KeyCert { export class KeyCert {
key: string; key: string;
cert: string; cert: string;
authservice: AuthService; // authservice: SshAuthzServer;
} }
export class AuthService { export class SshAuthzServer {
base: string; base: string;
authorise: string; authorise: string;
sign: string; sign: string;
client_id: string; client_id: string;
name: string;
icon: string;
scope: string;
logout: string;
} }
...@@ -9,9 +9,11 @@ ...@@ -9,9 +9,11 @@
{{ jobdata.state }} {{ jobdata.state }}
</td> </td>
<td withdt="10%"> <td withdt="10%">
<button mat-button (click)="onCancel()">