Merge branch 'spencer/refactor-http-requests' into 'master'
Replace XMLHttpRequests with Fetch API. See merge request grassrootseconomics/cic-staff-client!22
This commit is contained in:
commit
51f4f052c7
@ -32,14 +32,11 @@
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"src/styles.scss",
|
||||
"node_modules/datatables.net-dt/css/jquery.dataTables.css",
|
||||
"node_modules/bootstrap/dist/css/bootstrap.min.css"
|
||||
],
|
||||
"scripts": [
|
||||
"node_modules/jquery/dist/jquery.js",
|
||||
"node_modules/datatables.net/js/jquery.dataTables.js",
|
||||
"node_modules/bootstrap/dist/js/bootstrap.js",
|
||||
"node_modules/block-syncer/dist/worker_ondemand.js"
|
||||
"node_modules/bootstrap/dist/js/bootstrap.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
|
2858
package-lock.json
generated
2858
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@ -33,30 +33,19 @@
|
||||
"@angular/platform-browser-dynamic": "~10.2.0",
|
||||
"@angular/router": "~10.2.0",
|
||||
"@angular/service-worker": "~10.2.0",
|
||||
"@cicnet/schemas-data-validator": "*",
|
||||
"@popperjs/core": "^2.5.4",
|
||||
"angular-datatables": "^9.0.2",
|
||||
"block-syncer": "^0.2.4",
|
||||
"bootstrap": "^4.5.3",
|
||||
"chart.js": "^2.9.4",
|
||||
"cic-client": "0.1.4",
|
||||
"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",
|
||||
"http-server": "^0.12.3",
|
||||
"jquery": "^3.5.1",
|
||||
"mocha": "^8.2.1",
|
||||
"moolb": "^0.1.0",
|
||||
"ng2-charts": "^2.4.2",
|
||||
"ngx-logger": "^4.2.1",
|
||||
"openpgp": "^4.10.10",
|
||||
"popper.js": "^1.16.1",
|
||||
"rxjs": "~6.6.0",
|
||||
"sha3": "^2.1.4",
|
||||
"tslib": "^2.0.0",
|
||||
"vcard-parser": "^1.0.0",
|
||||
"vcards-js": "^2.10.0",
|
||||
"web3": "^1.3.0",
|
||||
"zone.js": "~0.10.2"
|
||||
},
|
||||
|
@ -87,3 +87,10 @@ export class GlobalErrorHandler extends ErrorHandler {
|
||||
return isWarning;
|
||||
}
|
||||
}
|
||||
|
||||
export function rejectBody(error): { status: any; statusText: any } {
|
||||
return {
|
||||
status: error.status,
|
||||
statusText: error.statusText,
|
||||
};
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
import { rejectBody } from '@app/_helpers/global-error-handler';
|
||||
|
||||
function HttpGetter(): void {}
|
||||
|
||||
HttpGetter.prototype.get = (filename) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const xhr: XMLHttpRequest = new XMLHttpRequest();
|
||||
xhr.addEventListener('load', (e) => {
|
||||
if (xhr.status === 200) {
|
||||
resolve(xhr.responseText);
|
||||
return;
|
||||
fetch(filename).then((response) => {
|
||||
if (response.ok) {
|
||||
resolve(response.json());
|
||||
} else {
|
||||
reject(rejectBody(response));
|
||||
}
|
||||
reject('failed with status ' + xhr.status + ': ' + xhr.statusText);
|
||||
return;
|
||||
});
|
||||
xhr.open('GET', filename);
|
||||
xhr.send();
|
||||
});
|
||||
|
||||
export { HttpGetter };
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { validatePerson, validateVcard } from 'cic-schemas-data-validator';
|
||||
import { validatePerson, validateVcard } from '@cicnet/schemas-data-validator';
|
||||
|
||||
async function personValidation(person: any): Promise<void> {
|
||||
const personValidationErrors: any = await validatePerson(person);
|
||||
|
@ -6,14 +6,13 @@ import { LoggingService } from '@app/_services/logging.service';
|
||||
import { MutableKeyStore, MutablePgpKeyStore } from '@app/_pgp';
|
||||
import { ErrorDialogService } from '@app/_services/error-dialog.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpError } from '@app/_helpers/global-error-handler';
|
||||
import { HttpError, rejectBody } from '@app/_helpers/global-error-handler';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthService {
|
||||
sessionToken: any;
|
||||
sessionLoginCount: number = 0;
|
||||
mutableKeyStore: MutableKeyStore;
|
||||
|
||||
constructor(
|
||||
@ -39,73 +38,75 @@ export class AuthService {
|
||||
document.getElementById('state').innerHTML = s;
|
||||
}
|
||||
|
||||
getWithToken(): void {
|
||||
const xhr: XMLHttpRequest = new XMLHttpRequest();
|
||||
xhr.responseType = 'text';
|
||||
xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1));
|
||||
xhr.setRequestHeader('Authorization', 'Bearer ' + this.sessionToken);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.setRequestHeader('x-cic-automerge', 'none');
|
||||
xhr.addEventListener('load', (e) => {
|
||||
if (xhr.status === 401) {
|
||||
throw new Error('login rejected');
|
||||
}
|
||||
this.sessionLoginCount++;
|
||||
this.setState('Click button to log in');
|
||||
return;
|
||||
getWithToken(): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const headers = {
|
||||
Authorization: 'Bearer ' + this.sessionToken,
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
'x-cic-automerge': 'none',
|
||||
};
|
||||
const options = {
|
||||
headers,
|
||||
};
|
||||
fetch(environment.cicMetaUrl, options).then((response) => {
|
||||
if (response.status === 401) {
|
||||
return reject(rejectBody(response));
|
||||
}
|
||||
return resolve(true);
|
||||
});
|
||||
});
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
// TODO rename to send signed challenge and set session. Also separate these responsibilities
|
||||
sendResponse(hobaResponseEncoded: any): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr: XMLHttpRequest = new XMLHttpRequest();
|
||||
xhr.responseType = 'text';
|
||||
xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1));
|
||||
xhr.setRequestHeader('Authorization', 'HOBA ' + hobaResponseEncoded);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.setRequestHeader('x-cic-automerge', 'none');
|
||||
xhr.addEventListener('load', (e) => {
|
||||
if (xhr.status !== 200) {
|
||||
const error = new HttpError(xhr.statusText, xhr.status);
|
||||
return reject(error);
|
||||
const headers = {
|
||||
Authorization: 'HOBA ' + hobaResponseEncoded,
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
'x-cic-automerge': 'none',
|
||||
};
|
||||
const options = {
|
||||
headers,
|
||||
};
|
||||
fetch(environment.cicMetaUrl, options).then((response) => {
|
||||
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);
|
||||
this.sessionLoginCount++;
|
||||
this.setState('Click button to log in');
|
||||
return resolve(true);
|
||||
});
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
getChallenge(): void {
|
||||
const xhr: XMLHttpRequest = new XMLHttpRequest();
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1));
|
||||
xhr.onload = async (e) => {
|
||||
if (xhr.status === 401) {
|
||||
const authHeader = xhr.getResponseHeader('WWW-Authenticate');
|
||||
const o = hobaParseChallengeHeader(authHeader);
|
||||
this.loginResponse(o);
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
getChallenge(): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(environment.cicMetaUrl).then(async (response) => {
|
||||
if (response.status === 401) {
|
||||
const authHeader: string = response.headers.get('WWW-Authenticate');
|
||||
return resolve(hobaParseChallengeHeader(authHeader));
|
||||
}
|
||||
if (!response.ok) {
|
||||
return reject(rejectBody(response));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
login(): boolean {
|
||||
async login(): Promise<boolean> {
|
||||
if (this.sessionToken !== undefined) {
|
||||
try {
|
||||
this.getWithToken();
|
||||
return true;
|
||||
const response: boolean = await this.getWithToken();
|
||||
return response === true;
|
||||
} catch (e) {
|
||||
this.loggingService.sendErrorLevelMessage('Login token failed', this, { error: e });
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
this.getChallenge();
|
||||
const o = await this.getChallenge();
|
||||
const response: boolean = await this.loginResponse(o);
|
||||
return response === true;
|
||||
} catch (e) {
|
||||
this.loggingService.sendErrorLevelMessage('Login challenge failed', this, { error: e });
|
||||
}
|
||||
@ -122,15 +123,15 @@ export class AuthService {
|
||||
environment.cicMetaUrl,
|
||||
this.mutableKeyStore
|
||||
);
|
||||
const sessionTokenResult: boolean = await this.sendResponse(r);
|
||||
const response: boolean = await this.sendResponse(r);
|
||||
resolve(response);
|
||||
} catch (error) {
|
||||
if (error instanceof HttpError) {
|
||||
if (error.status === 403) {
|
||||
this.errorDialogService.openDialog({
|
||||
message: 'You are not authorized to use this system',
|
||||
});
|
||||
}
|
||||
if (error.status === 401) {
|
||||
} else if (error.status === 401) {
|
||||
this.errorDialogService.openDialog({
|
||||
message:
|
||||
'Unable to authenticate with the service. ' +
|
||||
@ -139,9 +140,10 @@ export class AuthService {
|
||||
'staff@grassrootseconomics.net.',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// TODO define this error
|
||||
this.errorDialogService.openDialog({ message: 'Incorrect key passphrase.' });
|
||||
}
|
||||
// TODO define this error
|
||||
this.errorDialogService.openDialog({ message: 'Incorrect key passphrase.' });
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
@ -183,6 +185,7 @@ export class AuthService {
|
||||
|
||||
logout(): void {
|
||||
sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN'));
|
||||
localStorage.removeItem(btoa('CICADA_PRIVATE_KEY'));
|
||||
this.sessionToken = undefined;
|
||||
window.location.reload();
|
||||
}
|
||||
@ -194,12 +197,14 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async getPublicKeys(): Promise<any> {
|
||||
return await fetch(environment.publicKeysUrl).then((res) => {
|
||||
if (!res.ok) {
|
||||
// TODO does angular recommend an error interface?
|
||||
throw Error(`${res.statusText} - ${res.status}`);
|
||||
}
|
||||
return res.text();
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(environment.publicKeysUrl).then((res) => {
|
||||
if (!res.ok) {
|
||||
// TODO does angular recommend an error interface?
|
||||
return reject(rejectBody(res));
|
||||
}
|
||||
return resolve(res.text());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ import { AppComponent } from '@app/app.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
||||
import { GlobalErrorHandler, MockBackendProvider } from '@app/_helpers';
|
||||
import { DataTablesModule } from 'angular-datatables';
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { AuthGuard } from '@app/_guards';
|
||||
@ -23,7 +22,6 @@ import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
HttpClientModule,
|
||||
DataTablesModule,
|
||||
SharedModule,
|
||||
MatTableModule,
|
||||
LoggerModule.forRoot({
|
||||
|
@ -5,7 +5,6 @@ import { AccountsRoutingModule } from '@pages/accounts/accounts-routing.module';
|
||||
import { AccountsComponent } from '@pages/accounts/accounts.component';
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
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 { MatTableModule } from '@angular/material/table';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
@ -36,7 +35,6 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
CommonModule,
|
||||
AccountsRoutingModule,
|
||||
SharedModule,
|
||||
DataTablesModule,
|
||||
MatTableModule,
|
||||
MatSortModule,
|
||||
MatCheckboxModule,
|
||||
|
@ -4,7 +4,6 @@ import { CommonModule } from '@angular/common';
|
||||
import { PagesRoutingModule } from '@pages/pages-routing.module';
|
||||
import { PagesComponent } from '@pages/pages.component';
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
@ -17,7 +16,6 @@ import { MatCardModule } from '@angular/material/card';
|
||||
CommonModule,
|
||||
PagesRoutingModule,
|
||||
SharedModule,
|
||||
ChartsModule,
|
||||
MatButtonModule,
|
||||
MatFormFieldModule,
|
||||
MatSelectModule,
|
||||
|
@ -4,7 +4,6 @@ import { CommonModule } from '@angular/common';
|
||||
import { TransactionsRoutingModule } from '@pages/transactions/transactions-routing.module';
|
||||
import { TransactionsComponent } from '@pages/transactions/transactions.component';
|
||||
import { TransactionDetailsComponent } from '@pages/transactions/transaction-details/transaction-details.component';
|
||||
import { DataTablesModule } from 'angular-datatables';
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
@ -25,7 +24,6 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
imports: [
|
||||
CommonModule,
|
||||
TransactionsRoutingModule,
|
||||
DataTablesModule,
|
||||
SharedModule,
|
||||
MatTableModule,
|
||||
MatCheckboxModule,
|
||||
|
Loading…
Reference in New Issue
Block a user