access blocked on 403...still lots to do
This commit is contained in:
parent
2ef78f47a9
commit
246accb0b6
@ -22,6 +22,9 @@ Run `ng generate module module-name --route module-name --module app.module` to
|
|||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
set you environment variables - set these via environment variables as found in set-env.ts
|
||||||
|
// TODO create a .env file so people don't have to set these one-by-one
|
||||||
|
|
||||||
Run `npm run build:dev` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `build:prod` script for a production build.
|
Run `npm run build:dev` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `build:prod` script for a production build.
|
||||||
|
|
||||||
## Running unit tests
|
## Running unit tests
|
||||||
|
@ -3,6 +3,16 @@ import {LoggingService} from '@app/_services/logging.service';
|
|||||||
import {HttpErrorResponse} from '@angular/common/http';
|
import {HttpErrorResponse} from '@angular/common/http';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
|
|
||||||
|
// A generalized http repsonse error
|
||||||
|
export class HttpError extends Error {
|
||||||
|
public status: number
|
||||||
|
constructor(message, status) {
|
||||||
|
super(message);
|
||||||
|
this.status = status;
|
||||||
|
this.name = 'HttpError';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GlobalErrorHandler extends ErrorHandler {
|
export class GlobalErrorHandler extends ErrorHandler {
|
||||||
private sentencesForWarningLogging: string[] = [];
|
private sentencesForWarningLogging: string[] = [];
|
||||||
@ -14,13 +24,13 @@ export class GlobalErrorHandler extends ErrorHandler {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleError(error: any): void {
|
handleError(error: Error): void {
|
||||||
this.logError(error);
|
this.logError(error);
|
||||||
const message = error.message ? error.message : error.toString();
|
const message = error.message ? error.message : error.toString();
|
||||||
|
|
||||||
if (error.status) {
|
// if (error.status) {
|
||||||
error = new Error(message);
|
// error = new Error(message);
|
||||||
}
|
// }
|
||||||
|
|
||||||
const errorTraceString = `Error message:\n${message}.\nStack trace: ${error.stack}`;
|
const errorTraceString = `Error message:\n${message}.\nStack trace: ${error.stack}`;
|
||||||
|
|
||||||
|
@ -20,29 +20,30 @@ export class ErrorInterceptor implements HttpInterceptor {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||||
return next.handle(request).pipe(
|
// return next.handle(request).pipe(
|
||||||
catchError((err: HttpErrorResponse) => {
|
// catchError((err: HttpErrorResponse) => {
|
||||||
let errorMessage;
|
// let errorMessage;
|
||||||
if (err.error instanceof ErrorEvent) {
|
// if (err.error instanceof ErrorEvent) {
|
||||||
// A client-side or network error occurred. Handle it accordingly.
|
// // A client-side or network error occurred. Handle it accordingly.
|
||||||
errorMessage = `An error occurred: ${err.error.message}`;
|
// errorMessage = `An error occurred: ${err.error.message}`;
|
||||||
} else {
|
// } else {
|
||||||
// The backend returned an unsuccessful response code.
|
// // The backend returned an unsuccessful response code.
|
||||||
// The response body may contain clues as to what went wrong.
|
// // The response body may contain clues as to what went wrong.
|
||||||
errorMessage = `Backend returned code ${err.status}, body was: ${JSON.stringify(err.error)}`;
|
// errorMessage = `Backend returned code ${err.status}, body was: ${JSON.stringify(err.error)}`;
|
||||||
}
|
// }
|
||||||
this.loggingService.sendErrorLevelMessage(errorMessage, this, {error: err});
|
// this.loggingService.sendErrorLevelMessage(errorMessage, this, {error: err});
|
||||||
switch (err.status) {
|
// switch (err.status) {
|
||||||
case 401: // unauthorized
|
// case 401: // unauthorized
|
||||||
this.router.navigateByUrl('/auth').then();
|
// this.router.navigateByUrl('/auth').then();
|
||||||
break;
|
// break;
|
||||||
case 403: // forbidden
|
// case 403: // forbidden
|
||||||
location.reload(true);
|
// location.reload(true);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
// Return an observable with a user-facing error message.
|
// // Return an observable with a user-facing error message.
|
||||||
return throwError(err);
|
// return throwError(err);
|
||||||
})
|
// })
|
||||||
);
|
// );
|
||||||
|
return next.handle(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,20 +18,21 @@ export class LoggingInterceptor implements HttpInterceptor {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||||
this.loggingService.sendInfoLevelMessage(request);
|
return next.handle(request);
|
||||||
const startTime = Date.now();
|
// this.loggingService.sendInfoLevelMessage(request);
|
||||||
let status: string;
|
// const startTime = Date.now();
|
||||||
|
// let status: string;
|
||||||
|
|
||||||
return next.handle(request).pipe(tap(event => {
|
// return next.handle(request).pipe(tap(event => {
|
||||||
status = '';
|
// status = '';
|
||||||
if (event instanceof HttpResponse) {
|
// if (event instanceof HttpResponse) {
|
||||||
status = 'succeeded';
|
// status = 'succeeded';
|
||||||
}
|
// }
|
||||||
}, error => status = 'failed'),
|
// }, error => status = 'failed'),
|
||||||
finalize(() => {
|
// finalize(() => {
|
||||||
const elapsedTime = Date.now() - startTime;
|
// const elapsedTime = Date.now() - startTime;
|
||||||
const message = `${request.method} request for ${request.urlWithParams} ${status} in ${elapsedTime} ms`;
|
// const message = `${request.method} request for ${request.urlWithParams} ${status} in ${elapsedTime} ms`;
|
||||||
this.loggingService.sendInfoLevelMessage(message);
|
// this.loggingService.sendInfoLevelMessage(message);
|
||||||
}));
|
// }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { hobaParseChallengeHeader } from '@src/assets/js/hoba.js';
|
import { hobaParseChallengeHeader } from '@src/assets/js/hoba.js';
|
||||||
import { signChallenge } from '@src/assets/js/hoba-pgp.js';
|
import { signChallenge } from '@src/assets/js/hoba-pgp.js';
|
||||||
import {environment} from '@src/environments/environment';
|
import { environment } from '@src/environments/environment';
|
||||||
import {LoggingService} from '@app/_services/logging.service';
|
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 {Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
import { HttpError } from '@app/_helpers/global-error-handler';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -53,24 +54,28 @@ export class AuthService {
|
|||||||
xhr.send();
|
xhr.send();
|
||||||
}
|
}
|
||||||
|
|
||||||
sendResponse(hobaResponseEncoded): void {
|
//TODO renmae to send signed challenge and set session. Also seperate these responsibilities
|
||||||
const xhr = new XMLHttpRequest();
|
sendResponse(hobaResponseEncoded): Promise<boolean> {
|
||||||
xhr.responseType = 'text';
|
return new Promise((resolve, reject) => {
|
||||||
xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1));
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.setRequestHeader('Authorization', 'HOBA ' + hobaResponseEncoded);
|
xhr.responseType = 'text';
|
||||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
xhr.open('GET', environment.cicMetaUrl + window.location.search.substring(1));
|
||||||
xhr.setRequestHeader('x-cic-automerge', 'none');
|
xhr.setRequestHeader('Authorization', 'HOBA ' + hobaResponseEncoded);
|
||||||
xhr.addEventListener('load', (e) => {
|
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||||
if (xhr.status === 401) {
|
xhr.setRequestHeader('x-cic-automerge', 'none');
|
||||||
throw new Error('login rejected');
|
xhr.addEventListener('load', (e) => {
|
||||||
}
|
if (xhr.status !== 200) {
|
||||||
this.sessionToken = xhr.getResponseHeader('Token');
|
const error = new HttpError(xhr.statusText, xhr.status);
|
||||||
sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), this.sessionToken);
|
return reject(error);
|
||||||
this.sessionLoginCount++;
|
}
|
||||||
this.setState('Click button to log in');
|
this.sessionToken = xhr.getResponseHeader('Token');
|
||||||
return;
|
sessionStorage.setItem(btoa('CICADA_SESSION_TOKEN'), this.sessionToken);
|
||||||
});
|
this.sessionLoginCount++;
|
||||||
xhr.send();
|
this.setState('Click button to log in');
|
||||||
|
return resolve(true);
|
||||||
|
});
|
||||||
|
xhr.send();
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getChallenge(): void {
|
getChallenge(): void {
|
||||||
@ -81,7 +86,7 @@ export class AuthService {
|
|||||||
if (xhr.status === 401) {
|
if (xhr.status === 401) {
|
||||||
const authHeader = xhr.getResponseHeader('WWW-Authenticate');
|
const authHeader = xhr.getResponseHeader('WWW-Authenticate');
|
||||||
const o = hobaParseChallengeHeader(authHeader);
|
const o = hobaParseChallengeHeader(authHeader);
|
||||||
await this.loginResponse(o);
|
this.loginResponse(o);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.send();
|
xhr.send();
|
||||||
@ -108,12 +113,31 @@ export class AuthService {
|
|||||||
|
|
||||||
|
|
||||||
async loginResponse(o): Promise<any> {
|
async loginResponse(o): Promise<any> {
|
||||||
try {
|
return new Promise(async(resolve, reject) => {
|
||||||
const r = await signChallenge(o.challenge, o.realm, environment.cicMetaUrl, this.mutableKeyStore);
|
try {
|
||||||
this.sendResponse(r);
|
const r = await signChallenge(o.challenge,
|
||||||
} catch (error) {
|
o.realm,
|
||||||
this.errorDialogService.openDialog({message: 'Incorrect key passphrase.'});
|
environment.cicMetaUrl,
|
||||||
}
|
this.mutableKeyStore);
|
||||||
|
const sessionTokenResult = await this.sendResponse(r);
|
||||||
|
} 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) {
|
||||||
|
this.errorDialogService.openDialog({ message: 'Unable to authenticate with the service. ' +
|
||||||
|
'Please speak with the staff at Grassroots ' +
|
||||||
|
'Economics for requesting access ' +
|
||||||
|
'staff@grassrootseconomics.net.' })
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO define this error
|
||||||
|
this.errorDialogService.openDialog({message: 'Incorrect key passphrase.'});
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loginView(): void {
|
loginView(): void {
|
||||||
@ -157,8 +181,16 @@ export class AuthService {
|
|||||||
return trustedUsers;
|
return trustedUsers;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPublicKeys(): Observable<any> {
|
async getPublicKeys(): Promise<any> {
|
||||||
return this.httpClient.get(`${environment.publicKeysUrl}`, {responseType: 'text'});
|
const data = 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 data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPrivateKeys(): Promise<void> {
|
async getPrivateKeys(): Promise<void> {
|
||||||
|
@ -21,12 +21,20 @@ export class AppComponent {
|
|||||||
private errorDialogService: ErrorDialogService
|
private errorDialogService: ErrorDialogService
|
||||||
) {
|
) {
|
||||||
(async () => {
|
(async () => {
|
||||||
await this.authService.mutableKeyStore.loadKeyring();
|
try {
|
||||||
this.authService.getPublicKeys()
|
await this.authService.mutableKeyStore.loadKeyring();
|
||||||
.pipe(catchError(async (error) => {
|
// this.authService.getPublicKeys()
|
||||||
this.loggingService.sendErrorLevelMessage('Unable to load trusted public keys.', this, {error});
|
// .pipe(catchError(async (error) => {
|
||||||
this.errorDialogService.openDialog({message: 'Trusted keys endpoint can\'t be reached. Please try again later.'});
|
// this.loggingService.sendErrorLevelMessage('Unable to load trusted public keys.', this, {error});
|
||||||
})).subscribe(this.authService.mutableKeyStore.importPublicKey);
|
// this.errorDialogService.openDialog({message: 'Trusted keys endpoint can\'t be reached. Please try again later.'});
|
||||||
|
// })).subscribe(this.authService.mutableKeyStore.importPublicKey);
|
||||||
|
const publicKeys = await this.authService.getPublicKeys()
|
||||||
|
await this.authService.mutableKeyStore.importPublicKey(publicKeys);
|
||||||
|
} catch(error) {
|
||||||
|
this.errorDialogService.openDialog({message: 'Trusted keys endpoint can\'t be reached. Please try again later.'});
|
||||||
|
// TODO do something to halt user progress...show a sad cicada page 🦗?
|
||||||
|
}
|
||||||
|
|
||||||
})();
|
})();
|
||||||
this.mediaQuery.addListener(this.onResize);
|
this.mediaQuery.addListener(this.onResize);
|
||||||
this.onResize(this.mediaQuery);
|
this.onResize(this.mediaQuery);
|
||||||
|
@ -1,3 +1,16 @@
|
|||||||
|
import {NgxLoggerLevel} from 'ngx-logger';
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false
|
production: false,
|
||||||
|
bloxbergChainId: 8996,
|
||||||
|
logLevel: NgxLoggerLevel.ERROR,
|
||||||
|
serverLogLevel: NgxLoggerLevel.OFF,
|
||||||
|
loggingUrl: 'http://localhost:8000',
|
||||||
|
cicMetaUrl: 'https://meta.dev.grassrootseconomics.net',
|
||||||
|
publicKeysUrl: 'http://localhost:8080/.well-known/publickeys',
|
||||||
|
cicCacheUrl: 'https://cache.dev.grassrootseconomics.net',
|
||||||
|
web3Provider: 'ws://localhost:63546',
|
||||||
|
cicUssdUrl: 'https://ussd.dev.grassrootseconomics.net',
|
||||||
|
registryAddress: '0xAf1B487491073C2d49136Db3FD87E293302CF839',
|
||||||
|
trustedDeclaratorAddress: '0xEb3907eCad74a0013c259D5874AE7f22DcBcC95C'
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user