Commit 2d6dc3c0 authored by Chris Hines's avatar Chris Hines
Browse files

Merge branch 'dev' into 'test'

Dev

See merge request !92
parents 2933b914 ffd9e64b
Pipeline #16493 passed with stages
in 9 minutes and 3 seconds
This diff is collapsed.
{ {
"name": "sv2", "name": "angular-electron",
"version": "0.0.0", "version": "9.0.4",
"license": "MIT", "description": "Angular 11 with Electron (Typescript + SASS + Hot Reload)",
"homepage": "https://github.com/maximegris/angular-electron",
"author": {
"name": "Maxime GRIS",
"email": "maxime.gris@gmail.com"
},
"keywords": [
"angular",
"angular 11",
"electron",
"nodejs",
"typescript",
"spectron",
"eslint",
"sass",
"windows",
"mac",
"linux"
],
"main": "main.js",
"private": true,
"scripts": { "scripts": {
"postinstall": "electron-builder install-app-deps",
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "npm-run-all -p electron:serve ng:serve",
"build": "ng build --prod", "build": "npm run electron:serve-tsc && ng build --base-href ./",
"test": "ng test", "build:dev": "npm run build -- -c dev",
"lint": "ng lint", "build:prod": "npm run build -- -c production",
"e2e": "ng e2e" "ng:serve": "ng serve -c web -o",
"electron:serve-tsc": "tsc -p tsconfig.serve.json",
"electron:serve": "wait-on tcp:4200 && npm run electron:serve-tsc && npx electron . --serve",
"electron:local": "npm run build:prod && npx electron .",
"electron:build": "npm run build:prod && electron-builder build --publish=never",
"test": "ng test --watch=false",
"test:watch": "ng test",
"e2e": "npm run build:prod && cross-env TS_NODE_PROJECT='e2e/tsconfig.e2e.json' mocha --timeout 300000 --require ts-node/register e2e/**/*.e2e.ts",
"version": "conventional-changelog -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md",
"lint": "ng lint"
}, },
"private": true,
"dependencies": { "dependencies": {
"@angular/animations": "9.1.12", "@angular/animations": "11.2.4",
"@angular/cdk": "^9.2.4", "@angular/cdk": "11.2.3",
"@angular/common": "9.1.12", "@angular/flex-layout": "11.0.0-beta.33",
"@angular/compiler": "9.1.12", "@angular/material": "11.2.3",
"@angular/core": "9.1.12", "buffer": "6.0.3",
"@angular/flex-layout": "^9.0.0-beta.31", "jwk-to-ssh": "1.2.0",
"@angular/forms": "9.1.12", "keypair": "1.0.2",
"@angular/material": "^9.2.4", "node-forge": "0.10.0",
"@angular/platform-browser": "9.1.12", "rxjs-compat": "6.6.6",
"@angular/platform-browser-dynamic": "9.1.12", "web-ext": "5.5.0"
"@angular/router": "9.1.12",
"buffer": "^5.5.0",
"core-js": "^3.6.4",
"jwk-to-ssh": "^1.2.0",
"keypair": "^1.0.1",
"node-forge": "^0.8.5",
"rxjs": "^6.6.3",
"rxjs-compat": "^6.5.4",
"tslib": "^1.10.0",
"web-ext": "^5.1.0",
"zone.js": "~0.10.2"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "~0.901.12", "@angular-builders/custom-webpack": "11.0.0",
"@angular/cli": "9.1.12", "@angular-devkit/build-angular": "0.1102.0",
"@angular/compiler-cli": "9.1.12", "@angular-eslint/builder": "1.2.0",
"@angular/language-service": "9.1.12", "@angular-eslint/eslint-plugin": "1.2.0",
"@types/jasmine": "^3.5.2", "@angular-eslint/eslint-plugin-template": "1.2.0",
"@types/jasminewd2": "^2.0.8", "@angular-eslint/schematics": "1.2.0",
"@types/node": "^12.11.1", "@angular-eslint/template-parser": "1.2.0",
"codelyzer": "^5.1.2", "@angular/cli": "11.2.0",
"jasmine-core": "~3.4.0", "@angular/common": "11.2.0",
"jasmine-spec-reporter": "~4.2.1", "@angular/compiler": "11.2.0",
"karma": "^5.2.2", "@angular/compiler-cli": "11.2.0",
"karma-chrome-launcher": "~2.2.0", "@angular/core": "11.2.0",
"karma-coverage-istanbul-reporter": "^2.1.1", "@angular/forms": "11.2.0",
"karma-jasmine": "~2.0.1", "@angular/language-service": "11.2.0",
"karma-jasmine-html-reporter": "^1.5.2", "@angular/platform-browser": "11.2.0",
"protractor": "^7.0.0", "@angular/platform-browser-dynamic": "11.2.0",
"ts-node": "~8.2.0", "@angular/router": "11.2.0",
"tslint": "~5.17.0", "@ngx-translate/core": "13.0.0",
"typescript": "3.8.3" "@ngx-translate/http-loader": "6.0.0",
} "@types/jasmine": "3.6.3",
"@types/jasminewd2": "2.0.8",
"@types/mocha": "8.2.0",
"@types/node": "12.12.6",
"@typescript-eslint/eslint-plugin": "4.15.0",
"@typescript-eslint/eslint-plugin-tslint": "4.15.0",
"@typescript-eslint/parser": "4.15.0",
"chai": "4.2.0",
"conventional-changelog-cli": "2.1.1",
"core-js": "3.6.5",
"cross-env": "7.0.3",
"electron": "11.2.0",
"electron-builder": "22.9.1",
"electron-reload": "1.5.0",
"eslint": "7.20.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsdoc": "31.6.1",
"eslint-plugin-prefer-arrow": "1.2.3",
"jasmine-core": "3.6.0",
"jasmine-spec-reporter": "6.0.0",
"karma": "6.1.1",
"karma-coverage-istanbul-reporter": "3.0.3",
"karma-electron": "6.3.3",
"karma-jasmine": "4.0.1",
"karma-jasmine-html-reporter": "1.5.4",
"mocha": "8.2.1",
"npm-run-all": "4.1.5",
"rxjs": "6.6.3",
"spectron": "13.0.0",
"ts-node": "9.1.1",
"tslib": "2.1.0",
"typescript": "4.0.5",
"wait-on": "5.0.1",
"webdriver-manager": "12.1.8",
"zone.js": "0.10.3"
},
"engines": {
"node": ">=10.13.0"
},
"browserslist": [
"chrome 83"
]
} }
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { AboutUsComponent } from './aboutus.component'; import { AboutUsComponent } from './aboutus.component';
...@@ -6,7 +6,7 @@ describe('AboutUsComponent', () => { ...@@ -6,7 +6,7 @@ describe('AboutUsComponent', () => {
let component: AboutUsComponent; let component: AboutUsComponent;
let fixture: ComponentFixture<AboutUsComponent>; let fixture: ComponentFixture<AboutUsComponent>;
beforeEach(async(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ AboutUsComponent ] declarations: [ AboutUsComponent ]
}) })
......
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import {BackendSelectionService } from '../backend-selection.service';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import {OverlayContainer} from '@angular/cdk/overlay';
import { AuthorisationService } from '../authorisation.service';
import { ComputesitesService } from '../computesites.service';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { MatButtonToggleGroup } from '@angular/material/button-toggle'; import { MatButtonToggleGroup } from '@angular/material/button-toggle';
......
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { AccountinfoComponent } from './accountinfo.component'; import { AccountinfoComponent } from './accountinfo.component';
...@@ -6,7 +6,7 @@ describe('AccountinfoComponent', () => { ...@@ -6,7 +6,7 @@ describe('AccountinfoComponent', () => {
let component: AccountinfoComponent; let component: AccountinfoComponent;
let fixture: ComponentFixture<AccountinfoComponent>; let fixture: ComponentFixture<AccountinfoComponent>;
beforeEach(async(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ AccountinfoComponent ] declarations: [ AccountinfoComponent ]
}) })
......
...@@ -32,6 +32,7 @@ const routes: Routes = [ ...@@ -32,6 +32,7 @@ const routes: Routes = [
// { path: 'finishlaunch', component: LauncherComponent}, // { path: 'finishlaunch', component: LauncherComponent},
//{ path: 'cancellaunch', component: LauncherComponent}, //{ path: 'cancellaunch', component: LauncherComponent},
{ path: 'sshauthz_callback', component: KeygenComponent}, { path: 'sshauthz_callback', component: KeygenComponent},
{ path: 'sshauthz_callback*', component: KeygenComponent},
{ path: 'transfer', component: TransferComponent }, { path: 'transfer', component: TransferComponent },
{ path: 'noaccount/:site', component: NoaccountComponent}, { path: 'noaccount/:site', component: NoaccountComponent},
{ path: '**', component: LauncherComponent}, { path: '**', component: LauncherComponent},
...@@ -45,7 +46,7 @@ const routes: Routes = [ ...@@ -45,7 +46,7 @@ const routes: Routes = [
@NgModule({ @NgModule({
imports: [ imports: [
RouterModule.forRoot(routes) RouterModule.forRoot(routes, { relativeLinkResolution: 'legacy' })
], ],
exports: [ RouterModule ], exports: [ RouterModule ],
}) })
......
:host {
}
\ No newline at end of file
import { TestBed, async } from '@angular/core/testing'; import { TestBed, waitForAsync } from '@angular/core/testing';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
describe('AppComponent', () => { describe('AppComponent', () => {
beforeEach(async(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
declarations: [ declarations: [
AppComponent AppComponent
], ],
}).compileComponents(); }).compileComponents();
})); }));
it('should create the app', async(() => { it('should create the app', waitForAsync(() => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance; const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy(); expect(app).toBeTruthy();
})); }));
it(`should have as title 'app'`, async(() => { it(`should have as title 'app'`, waitForAsync(() => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance; const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app'); expect(app.title).toEqual('app');
})); }));
it('should render title in a h1 tag', async(() => { it('should render title in a h1 tag', waitForAsync(() => {
const fixture = TestBed.createComponent(AppComponent); const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges(); fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement; const compiled = fixture.debugElement.nativeElement;
......
...@@ -2,7 +2,8 @@ import { Component } from '@angular/core'; ...@@ -2,7 +2,8 @@ import { Component } from '@angular/core';
import { TesService } from './tes.service'; import { TesService } from './tes.service';
import { AuthorisationService} from './authorisation.service'; import { AuthorisationService} from './authorisation.service';
import { ComputesitesService} from './computesites.service'; import { ComputesitesService} from './computesites.service';
import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import { IpcService } from './ipc.service';
import {BehaviorSubject} from 'rxjs';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { Computesite, Health } from './computesite'; import { Computesite, Health } from './computesite';
import {SettingsService } from './settings.service'; import {SettingsService } from './settings.service';
...@@ -35,7 +36,9 @@ export class AppComponent { ...@@ -35,7 +36,9 @@ export class AppComponent {
private overlayContainer: OverlayContainer, private overlayContainer: OverlayContainer,
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private notifications: NotificationsService) { private notifications: NotificationsService,
private ipcService: IpcService
) {
}; };
...@@ -49,8 +52,19 @@ export class AppComponent { ...@@ -49,8 +52,19 @@ export class AppComponent {
this.authService.loggedOutAuthZ.pipe( this.authService.loggedOutAuthZ.pipe(
isdefined isdefined
).subscribe((v) => { this.loggedout = (<[]>v).length } ); ).subscribe((v) => { this.loggedout = (<[]>v).length } );
this.ipcService.once('oauth2-redirect',(event, ...args: any[]) => this.route_to_keygen(event, args));
} }
route_to_keygen(event, ... args: any[]) {
console.log('calling back from oauth', args);
var url: string = args[0][0];
let fragment = "#" + url.split('#')[1];
console.log('app.component is updating fragment');
this.authService.updateFragment(fragment);
this.router.navigate(['/sshauthz_callback']);
}
toggleMenu() { toggleMenu() {
if (this.settingsService.menuToggle$.value == true) { if (this.settingsService.menuToggle$.value == true) {
this.settingsService.menuToggle$.next(false); this.settingsService.menuToggle$.next(false);
......
...@@ -34,6 +34,7 @@ import { TesService} from './tes.service'; ...@@ -34,6 +34,7 @@ import { TesService} from './tes.service';
import { BrowserWindowService } from './browser-window.service'; import { BrowserWindowService } from './browser-window.service';
import { BackendSelectionService} from './backend-selection.service'; import { BackendSelectionService} from './backend-selection.service';
import { SubmitAppService } from './submit-app.service'; import { SubmitAppService } from './submit-app.service';
import { IpcService } from './ipc.service';
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { FormsModule, ReactiveFormsModule } 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';
...@@ -70,6 +71,7 @@ import { OurServicesComponent } from './ourservices/ourservices.component'; ...@@ -70,6 +71,7 @@ import { OurServicesComponent } from './ourservices/ourservices.component';
import { ContactUsComponent } from './contactus/contactus.component'; import { ContactUsComponent } from './contactus/contactus.component';
import { NoaccountComponent } from './noaccount/noaccount.component'; import { NoaccountComponent } from './noaccount/noaccount.component';
import { WarndialogComponent } from './warndialog/warndialog.component'; import { WarndialogComponent } from './warndialog/warndialog.component';
import { ExterndialogComponent } from './externdialog/externdialog.component';
// import { FileExplorerModule } from './file-explorer/file-explorer.module'; // import { FileExplorerModule } from './file-explorer/file-explorer.module';
...@@ -103,6 +105,7 @@ import { WarndialogComponent } from './warndialog/warndialog.component'; ...@@ -103,6 +105,7 @@ import { WarndialogComponent } from './warndialog/warndialog.component';
ContactUsComponent, ContactUsComponent,
NoaccountComponent, NoaccountComponent,
WarndialogComponent, WarndialogComponent,
ExterndialogComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
...@@ -143,7 +146,7 @@ import { WarndialogComponent } from './warndialog/warndialog.component'; ...@@ -143,7 +146,7 @@ import { WarndialogComponent } from './warndialog/warndialog.component';
], ],
entryComponents: [ LogoutdialogComponent, ModaldialogComponent, WarndialogComponent], entryComponents: [ LogoutdialogComponent, ModaldialogComponent, WarndialogComponent],
//providers: [ StrudelappsService, ComputesitesService, TesService, SubmitAppService, MatDialog, AuthorisationService,BackendSelectionService,SettingsService], //providers: [ StrudelappsService, ComputesitesService, TesService, SubmitAppService, MatDialog, AuthorisationService,BackendSelectionService,SettingsService],
providers: [NotificationsService, ComputesitesService, TesService, BrowserWindowService, SubmitAppService, MatDialog, AuthorisationService,BackendSelectionService,SettingsService, JobsService], providers: [NotificationsService, ComputesitesService, TesService, BrowserWindowService, SubmitAppService, MatDialog, AuthorisationService,BackendSelectionService,SettingsService, JobsService, IpcService],
bootstrap: [AppComponent] bootstrap: [AppComponent]
}) })
export class AppModule { } export class AppModule { }
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; import { HttpClientModule, HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { ActivatedRoute, Router } from '@angular/router';
import { Observable, Subject, BehaviorSubject } from 'rxjs'; import { Observable, Subject, BehaviorSubject } from 'rxjs';
import { catchError, map, tap, take,filter,skip, switchMap } from 'rxjs/operators'; import { catchError, map, tap, take,filter,skip, switchMap } from 'rxjs/operators';
import {LocationStrategy, Location} from '@angular/common'; import {LocationStrategy, Location} from '@angular/common';
...@@ -9,8 +10,11 @@ import * as forge from "node-forge"; ...@@ -9,8 +10,11 @@ import * as forge from "node-forge";
import { Identity, AuthToken, KeyCert, SshAuthzServer } from './identity'; import { Identity, AuthToken, KeyCert, SshAuthzServer } from './identity';
import { APIServer } from './apiserver'; import { APIServer } from './apiserver';
import {BackendSelectionService} from './backend-selection.service'; import {BackendSelectionService} from './backend-selection.service';
import { throwError, of, combineLatest } from 'rxjs'; import { throwError, of, combineLatest, from } from 'rxjs';
import {NotificationsService} from './notifications.service'; import {NotificationsService} from './notifications.service';
import { IpcService } from './ipc.service';
import * as jwktossh from "jwk-to-ssh";
export class SshauthzServer {} export class SshauthzServer {}
...@@ -30,13 +34,16 @@ export class AuthorisationService { ...@@ -30,13 +34,16 @@ export class AuthorisationService {
// private keyCert: Subject<KeyCert>; // private keyCert: Subject<KeyCert>;
public backendURI: string; public backendURI: string;
public keys: KeyCert[]; public keys: KeyCert[];
private fragment$: BehaviorSubject<string> = new BehaviorSubject(null);
constructor(private http: HttpClient, constructor(private http: HttpClient,
private locationStrategy: LocationStrategy, private locationStrategy: LocationStrategy,
private router: Router,
private backendSelectionService: BackendSelectionService, private backendSelectionService: BackendSelectionService,
private location: Location, private location: Location,
private notifications: NotificationsService) { private notifications: NotificationsService,
private ipcService: IpcService) {
this.sshAuthzServers = new BehaviorSubject<SshAuthzServer[]>([]); this.sshAuthzServers = new BehaviorSubject<SshAuthzServer[]>([]);
this.loggedInAuthZ = new BehaviorSubject<SshAuthzServer[]>(null); this.loggedInAuthZ = new BehaviorSubject<SshAuthzServer[]>(null);
this.loggedOutAuthZ = new BehaviorSubject<SshAuthzServer[]>([]); this.loggedOutAuthZ = new BehaviorSubject<SshAuthzServer[]>([]);
...@@ -68,8 +75,14 @@ export class AuthorisationService { ...@@ -68,8 +75,14 @@ export class AuthorisationService {
authZ$.subscribe(([loggedin,loggedout]) => { this.loggedInAuthZ.next(loggedin); this.loggedOutAuthZ.next(loggedout); }) authZ$.subscribe(([loggedin,loggedout]) => { this.loggedInAuthZ.next(loggedin); this.loggedOutAuthZ.next(loggedout); })
this.loggedInAuthZ$ = authZ$[0]; this.loggedInAuthZ$ = authZ$[0];
this.loggedOutAuthZ$ = authZ$[1]; this.loggedOutAuthZ$ = authZ$[1];
this.initKeygenPipelines();
} }
updateFragment(frag) {
this.fragment$.next(frag);
}
storeLocalAuthZ(authz: any) { storeLocalAuthZ(authz: any) {
try { try {
...@@ -254,6 +267,15 @@ public getKeys(id?: Identity) { ...@@ -254,6 +267,15 @@ public getKeys(id?: Identity) {
public login(authservice: SshAuthzServer) { 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";
if (redirect_uri.includes("file:///")) {
console.log('altering redirect_uri');
console.log(redirect_uri);
console.log(window.location.origin);
console.log(this.locationStrategy.getBaseHref());
redirect_uri = "http://localhost:4200/sshauthz_callback";
}
console.log('redirect_uri will be',redirect_uri);
debugger;
let nonce=Math.random().toString(36).substring(2, 15) let nonce=Math.random().toString(36).substring(2, 15)
sessionStorage.setItem('authservice', JSON.stringify([authservice,nonce])); sessionStorage.setItem('authservice', JSON.stringify([authservice,nonce]));
...@@ -279,4 +301,148 @@ public getKeys(id?: Identity) { ...@@ -279,4 +301,148 @@ public getKeys(id?: Identity) {
// return of(result as T); // return of(result as T);
}; };
} }
initKeygenPipelines() {
const token$: Observable<AuthToken> = this.fragment$.pipe(
filter((v) => v !== null),
map((v) => this.extractToken(v)),
);
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"};
}),
)
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_sshauthz(token.sshauthzservice)),
);
let agent$ = combineLatest([keycert$.pipe(filter((v) => v !== null)),apiserver$]).pipe(
switchMap(([keycert,apiserver]) => this.addCert(keycert,apiserver)),
switchMap((_) => this.updateAgentContents()),
switchMap((_) => this.loggedInAuthZ),
switchMap((_) => of([null])),
);
agent$.subscribe( (res) => this.router.navigate([sessionStorage.getItem('path')]),
(err) => { console.log(err) ;
if (err.sshauthzservice !== undefined ) {
this.logout_sshauthz(err.sshauthzservice)
this.router.navigate(['/noaccount',err.sshauthzservice.name])
} else {
this.router.navigate(['/login'])}
} )
}
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');
}