Merge branch 'master' into spencer/docs

# Conflicts:
#	package-lock.json
#	src/app/_helpers/http-getter.ts
#	src/app/_helpers/schema-validation.ts
This commit is contained in:
Spencer Ofwiti 2021-05-17 09:21:57 +03:00
commit 32fe670487
11 changed files with 617 additions and 6778 deletions

View File

@ -32,14 +32,11 @@
"styles": [ "styles": [
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
"src/styles.scss", "src/styles.scss",
"node_modules/datatables.net-dt/css/jquery.dataTables.css",
"node_modules/bootstrap/dist/css/bootstrap.min.css" "node_modules/bootstrap/dist/css/bootstrap.min.css"
], ],
"scripts": [ "scripts": [
"node_modules/jquery/dist/jquery.js", "node_modules/jquery/dist/jquery.js",
"node_modules/datatables.net/js/jquery.dataTables.js", "node_modules/bootstrap/dist/js/bootstrap.js"
"node_modules/bootstrap/dist/js/bootstrap.js",
"node_modules/block-syncer/dist/worker_ondemand.js"
] ]
}, },
"configurations": { "configurations": {

7221
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -36,30 +36,19 @@
"@angular/platform-browser-dynamic": "~10.2.0", "@angular/platform-browser-dynamic": "~10.2.0",
"@angular/router": "~10.2.0", "@angular/router": "~10.2.0",
"@angular/service-worker": "~10.2.0", "@angular/service-worker": "~10.2.0",
"@cicnet/schemas-data-validator": "*",
"@popperjs/core": "^2.5.4", "@popperjs/core": "^2.5.4",
"angular-datatables": "^9.0.2",
"block-syncer": "^0.2.4",
"bootstrap": "^4.5.3", "bootstrap": "^4.5.3",
"chart.js": "^2.9.4",
"cic-client": "0.1.4", "cic-client": "0.1.4",
"cic-client-meta": "0.0.7-alpha.6", "cic-client-meta": "0.0.7-alpha.6",
"cic-schemas-data-validator": "^1.0.0-alpha.3",
"datatables.net": "^1.10.22",
"datatables.net-dt": "^1.10.22",
"ethers": "^5.0.31", "ethers": "^5.0.31",
"http-server": "^0.12.3", "http-server": "^0.12.3",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"mocha": "^8.2.1",
"moolb": "^0.1.0",
"ng2-charts": "^2.4.2",
"ngx-logger": "^4.2.1", "ngx-logger": "^4.2.1",
"openpgp": "^4.10.10",
"popper.js": "^1.16.1",
"rxjs": "~6.6.0", "rxjs": "~6.6.0",
"sha3": "^2.1.4", "sha3": "^2.1.4",
"tslib": "^2.0.0", "tslib": "^2.0.0",
"vcard-parser": "^1.0.0", "vcard-parser": "^1.0.0",
"vcards-js": "^2.10.0",
"web3": "^1.3.0", "web3": "^1.3.0",
"zone.js": "~0.10.2" "zone.js": "~0.10.2"
}, },

View File

@ -132,3 +132,10 @@ export class GlobalErrorHandler extends ErrorHandler {
} }
} }
} }
export function rejectBody(error): { status: any; statusText: any } {
return {
status: error.status,
statusText: error.statusText,
};
}

View File

@ -1,3 +1,5 @@
import { rejectBody } from '@app/_helpers/global-error-handler';
/** Provides an avenue of fetching resources via HTTP calls. */ /** Provides an avenue of fetching resources via HTTP calls. */
function HttpGetter(): void {} function HttpGetter(): void {}
@ -9,16 +11,14 @@ function HttpGetter(): void {}
*/ */
HttpGetter.prototype.get = (filename) => HttpGetter.prototype.get = (filename) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
const xhr: XMLHttpRequest = new XMLHttpRequest(); fetch(filename).then((response) => {
xhr.addEventListener('load', (e) => { if (response.ok) {
if (xhr.status === 200) { resolve(response.json());
resolve(xhr.responseText); } else {
return; reject(rejectBody(response));
} }
reject('failed with status ' + xhr.status + ': ' + xhr.statusText); return;
}); });
xhr.open('GET', filename);
xhr.send();
}); });
/** @exports */ /** @exports */

View File

@ -1,5 +1,5 @@
// Third party imports // Third party imports
import { validatePerson, validateVcard } from 'cic-schemas-data-validator'; import { validatePerson, validateVcard } from '@cicnet/schemas-data-validator';
/** /**
* Validates a person object against the defined Person schema. * Validates a person object against the defined Person schema.

View File

@ -6,14 +6,13 @@ import { LoggingService } from '@app/_services/logging.service';
import { MutableKeyStore, MutablePgpKeyStore } from '@app/_pgp'; import { MutableKeyStore, MutablePgpKeyStore } from '@app/_pgp';
import { ErrorDialogService } from '@app/_services/error-dialog.service'; import { ErrorDialogService } from '@app/_services/error-dialog.service';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { HttpError } from '@app/_helpers/global-error-handler'; import { HttpError, rejectBody } from '@app/_helpers/global-error-handler';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class AuthService { export class AuthService {
sessionToken: any; sessionToken: any;
sessionLoginCount: number = 0;
mutableKeyStore: MutableKeyStore; mutableKeyStore: MutableKeyStore;
constructor( constructor(
@ -39,73 +38,75 @@ export class AuthService {
document.getElementById('state').innerHTML = s; document.getElementById('state').innerHTML = s;
} }
getWithToken(): void { getWithToken(): Promise<boolean> {
const xhr: XMLHttpRequest = new XMLHttpRequest(); return new Promise((resolve, reject) => {
xhr.responseType = 'text'; const headers = {
xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1)); Authorization: 'Bearer ' + this.sessionToken,
xhr.setRequestHeader('Authorization', 'Bearer ' + this.sessionToken); 'Content-Type': 'application/json;charset=utf-8',
xhr.setRequestHeader('Content-Type', 'application/json'); 'x-cic-automerge': 'none',
xhr.setRequestHeader('x-cic-automerge', 'none'); };
xhr.addEventListener('load', (e) => { const options = {
if (xhr.status === 401) { headers,
throw new Error('login rejected'); };
fetch(environment.cicMetaUrl, options).then((response) => {
if (response.status === 401) {
return reject(rejectBody(response));
} }
this.sessionLoginCount++; return resolve(true);
this.setState('Click button to log in'); });
return;
}); });
xhr.send();
} }
// TODO rename to send signed challenge and set session. Also separate these responsibilities // TODO rename to send signed challenge and set session. Also separate these responsibilities
sendResponse(hobaResponseEncoded: any): Promise<boolean> { sendResponse(hobaResponseEncoded: any): Promise<boolean> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const xhr: XMLHttpRequest = new XMLHttpRequest(); const headers = {
xhr.responseType = 'text'; Authorization: 'HOBA ' + hobaResponseEncoded,
xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1)); 'Content-Type': 'application/json;charset=utf-8',
xhr.setRequestHeader('Authorization', 'HOBA ' + hobaResponseEncoded); 'x-cic-automerge': 'none',
xhr.setRequestHeader('Content-Type', 'application/json'); };
xhr.setRequestHeader('x-cic-automerge', 'none'); const options = {
xhr.addEventListener('load', (e) => { headers,
if (xhr.status !== 200) { };
const error = new HttpError(xhr.statusText, xhr.status); fetch(environment.cicMetaUrl, options).then((response) => {
return reject(error); if (response.status === 401) {
return reject(rejectBody(response));
} }
this.sessionToken = xhr.getResponseHeader('Token'); this.sessionToken = response.headers.get('Token');
sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), this.sessionToken); sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), this.sessionToken);
this.sessionLoginCount++;
this.setState('Click button to log in'); this.setState('Click button to log in');
return resolve(true); return resolve(true);
}); });
xhr.send();
}); });
} }
getChallenge(): void { getChallenge(): Promise<any> {
const xhr: XMLHttpRequest = new XMLHttpRequest(); return new Promise((resolve, reject) => {
xhr.responseType = 'arraybuffer'; fetch(environment.cicMetaUrl).then(async (response) => {
xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1)); if (response.status === 401) {
xhr.onload = async (e) => { const authHeader: string = response.headers.get('WWW-Authenticate');
if (xhr.status === 401) { return resolve(hobaParseChallengeHeader(authHeader));
const authHeader = xhr.getResponseHeader('WWW-Authenticate');
const o = hobaParseChallengeHeader(authHeader);
this.loginResponse(o);
} }
}; if (!response.ok) {
xhr.send(); return reject(rejectBody(response));
}
});
});
} }
login(): boolean { async login(): Promise<boolean> {
if (this.sessionToken !== undefined) { if (this.sessionToken !== undefined) {
try { try {
this.getWithToken(); const response: boolean = await this.getWithToken();
return true; return response === true;
} catch (e) { } catch (e) {
this.loggingService.sendErrorLevelMessage('Login token failed', this, { error: e }); this.loggingService.sendErrorLevelMessage('Login token failed', this, { error: e });
} }
} else { } else {
try { try {
this.getChallenge(); const o = await this.getChallenge();
const response: boolean = await this.loginResponse(o);
return response === true;
} catch (e) { } catch (e) {
this.loggingService.sendErrorLevelMessage('Login challenge failed', this, { error: e }); this.loggingService.sendErrorLevelMessage('Login challenge failed', this, { error: e });
} }
@ -122,15 +123,15 @@ export class AuthService {
environment.cicMetaUrl, environment.cicMetaUrl,
this.mutableKeyStore this.mutableKeyStore
); );
const sessionTokenResult: boolean = await this.sendResponse(r); const response: boolean = await this.sendResponse(r);
resolve(response);
} catch (error) { } catch (error) {
if (error instanceof HttpError) { if (error instanceof HttpError) {
if (error.status === 403) { if (error.status === 403) {
this.errorDialogService.openDialog({ this.errorDialogService.openDialog({
message: 'You are not authorized to use this system', message: 'You are not authorized to use this system',
}); });
} } else if (error.status === 401) {
if (error.status === 401) {
this.errorDialogService.openDialog({ this.errorDialogService.openDialog({
message: message:
'Unable to authenticate with the service. ' + 'Unable to authenticate with the service. ' +
@ -139,9 +140,10 @@ export class AuthService {
'staff@grassrootseconomics.net.', 'staff@grassrootseconomics.net.',
}); });
} }
} } else {
// TODO define this error // TODO define this error
this.errorDialogService.openDialog({ message: 'Incorrect key passphrase.' }); this.errorDialogService.openDialog({ message: 'Incorrect key passphrase.' });
}
resolve(false); resolve(false);
} }
}); });
@ -187,6 +189,7 @@ export class AuthService {
logout(): void { logout(): void {
sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN')); sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN'));
localStorage.removeItem(btoa('CICADA_PRIVATE_KEY'));
this.sessionToken = undefined; this.sessionToken = undefined;
window.location.reload(); window.location.reload();
} }
@ -198,12 +201,14 @@ export class AuthService {
} }
async getPublicKeys(): Promise<any> { async getPublicKeys(): Promise<any> {
return await fetch(environment.publicKeysUrl).then((res) => { return new Promise((resolve, reject) => {
fetch(environment.publicKeysUrl).then((res) => {
if (!res.ok) { if (!res.ok) {
// TODO does angular recommend an error interface? // TODO does angular recommend an error interface?
throw Error(`${res.statusText} - ${res.status}`); return reject(rejectBody(res));
} }
return res.text(); return resolve(res.text());
});
}); });
} }

View File

@ -6,7 +6,6 @@ import { AppComponent } from '@app/app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { GlobalErrorHandler, MockBackendProvider } from '@app/_helpers'; import { GlobalErrorHandler, MockBackendProvider } from '@app/_helpers';
import { DataTablesModule } from 'angular-datatables';
import { SharedModule } from '@app/shared/shared.module'; import { SharedModule } from '@app/shared/shared.module';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { AuthGuard } from '@app/_guards'; import { AuthGuard } from '@app/_guards';
@ -23,7 +22,6 @@ import { ServiceWorkerModule } from '@angular/service-worker';
AppRoutingModule, AppRoutingModule,
BrowserAnimationsModule, BrowserAnimationsModule,
HttpClientModule, HttpClientModule,
DataTablesModule,
SharedModule, SharedModule,
MatTableModule, MatTableModule,
LoggerModule.forRoot({ LoggerModule.forRoot({

View File

@ -5,7 +5,6 @@ import { AccountsRoutingModule } from '@pages/accounts/accounts-routing.module';
import { AccountsComponent } from '@pages/accounts/accounts.component'; import { AccountsComponent } from '@pages/accounts/accounts.component';
import { SharedModule } from '@app/shared/shared.module'; import { SharedModule } from '@app/shared/shared.module';
import { AccountDetailsComponent } from '@pages/accounts/account-details/account-details.component'; import { AccountDetailsComponent } from '@pages/accounts/account-details/account-details.component';
import { DataTablesModule } from 'angular-datatables';
import { CreateAccountComponent } from '@pages/accounts/create-account/create-account.component'; import { CreateAccountComponent } from '@pages/accounts/create-account/create-account.component';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatSortModule } from '@angular/material/sort'; import { MatSortModule } from '@angular/material/sort';
@ -36,7 +35,6 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
CommonModule, CommonModule,
AccountsRoutingModule, AccountsRoutingModule,
SharedModule, SharedModule,
DataTablesModule,
MatTableModule, MatTableModule,
MatSortModule, MatSortModule,
MatCheckboxModule, MatCheckboxModule,

View File

@ -4,7 +4,6 @@ import { CommonModule } from '@angular/common';
import { PagesRoutingModule } from '@pages/pages-routing.module'; import { PagesRoutingModule } from '@pages/pages-routing.module';
import { PagesComponent } from '@pages/pages.component'; import { PagesComponent } from '@pages/pages.component';
import { SharedModule } from '@app/shared/shared.module'; import { SharedModule } from '@app/shared/shared.module';
import { ChartsModule } from 'ng2-charts';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field'; import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select'; import { MatSelectModule } from '@angular/material/select';
@ -17,7 +16,6 @@ import { MatCardModule } from '@angular/material/card';
CommonModule, CommonModule,
PagesRoutingModule, PagesRoutingModule,
SharedModule, SharedModule,
ChartsModule,
MatButtonModule, MatButtonModule,
MatFormFieldModule, MatFormFieldModule,
MatSelectModule, MatSelectModule,

View File

@ -4,7 +4,6 @@ import { CommonModule } from '@angular/common';
import { TransactionsRoutingModule } from '@pages/transactions/transactions-routing.module'; import { TransactionsRoutingModule } from '@pages/transactions/transactions-routing.module';
import { TransactionsComponent } from '@pages/transactions/transactions.component'; import { TransactionsComponent } from '@pages/transactions/transactions.component';
import { TransactionDetailsComponent } from '@pages/transactions/transaction-details/transaction-details.component'; import { TransactionDetailsComponent } from '@pages/transactions/transaction-details/transaction-details.component';
import { DataTablesModule } from 'angular-datatables';
import { SharedModule } from '@app/shared/shared.module'; import { SharedModule } from '@app/shared/shared.module';
import { MatTableModule } from '@angular/material/table'; import { MatTableModule } from '@angular/material/table';
import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatCheckboxModule } from '@angular/material/checkbox';
@ -25,7 +24,6 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
imports: [ imports: [
CommonModule, CommonModule,
TransactionsRoutingModule, TransactionsRoutingModule,
DataTablesModule,
SharedModule, SharedModule,
MatTableModule, MatTableModule,
MatCheckboxModule, MatCheckboxModule,