From 8636cbdf995e0b06ed598f070379b8df7aa6bc0d Mon Sep 17 00:00:00 2001 From: Spencer Ofwiti Date: Mon, 28 Dec 2020 12:06:30 +0300 Subject: [PATCH] Add helper classes. - Add error state matcher for forms. - Add custom validator for forms. - Add error interceptor for incoming responses. - Add unsafe key store for holding private keys. --- .../custom-error-state-matcher.spec.ts | 7 ++++ .../_helpers/custom-error-state-matcher.ts | 9 ++++++ src/app/_helpers/custom.validator.spec.ts | 7 ++++ src/app/_helpers/custom.validator.ts | 22 +++++++++++++ src/app/_helpers/error.interceptor.spec.ts | 16 ++++++++++ src/app/_helpers/error.interceptor.ts | 25 +++++++++++++++ src/app/_helpers/index.ts | 4 +++ src/app/_helpers/unsafe-key-store.spec.ts | 7 ++++ src/app/_helpers/unsafe-key-store.ts | 32 +++++++++++++++++++ 9 files changed, 129 insertions(+) create mode 100644 src/app/_helpers/custom-error-state-matcher.spec.ts create mode 100644 src/app/_helpers/custom-error-state-matcher.ts create mode 100644 src/app/_helpers/custom.validator.spec.ts create mode 100644 src/app/_helpers/custom.validator.ts create mode 100644 src/app/_helpers/error.interceptor.spec.ts create mode 100644 src/app/_helpers/error.interceptor.ts create mode 100644 src/app/_helpers/index.ts create mode 100644 src/app/_helpers/unsafe-key-store.spec.ts create mode 100644 src/app/_helpers/unsafe-key-store.ts diff --git a/src/app/_helpers/custom-error-state-matcher.spec.ts b/src/app/_helpers/custom-error-state-matcher.spec.ts new file mode 100644 index 0000000..7a3b76c --- /dev/null +++ b/src/app/_helpers/custom-error-state-matcher.spec.ts @@ -0,0 +1,7 @@ +import { CustomErrorStateMatcher } from './custom-error-state-matcher'; + +describe('CustomErrorStateMatcher', () => { + it('should create an instance', () => { + expect(new CustomErrorStateMatcher()).toBeTruthy(); + }); +}); diff --git a/src/app/_helpers/custom-error-state-matcher.ts b/src/app/_helpers/custom-error-state-matcher.ts new file mode 100644 index 0000000..8728100 --- /dev/null +++ b/src/app/_helpers/custom-error-state-matcher.ts @@ -0,0 +1,9 @@ +import {ErrorStateMatcher} from '@angular/material/core'; +import {FormControl, FormGroupDirective, NgForm} from '@angular/forms'; + +export class CustomErrorStateMatcher implements ErrorStateMatcher{ + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { + const isSubmitted = form && form.submitted; + return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted)); + } +} diff --git a/src/app/_helpers/custom.validator.spec.ts b/src/app/_helpers/custom.validator.spec.ts new file mode 100644 index 0000000..565ab9c --- /dev/null +++ b/src/app/_helpers/custom.validator.spec.ts @@ -0,0 +1,7 @@ +import { Custom.Validator } from './custom.validator'; + +describe('Custom.Validator', () => { + it('should create an instance', () => { + expect(new Custom.Validator()).toBeTruthy(); + }); +}); diff --git a/src/app/_helpers/custom.validator.ts b/src/app/_helpers/custom.validator.ts new file mode 100644 index 0000000..25aa06a --- /dev/null +++ b/src/app/_helpers/custom.validator.ts @@ -0,0 +1,22 @@ +import {AbstractControl, ValidationErrors} from '@angular/forms'; + +export class CustomValidator { + static passwordMatchValidator(control: AbstractControl): void { + const password: string = control.get('password').value; + const confirmPassword: string = control.get('confirmPassword').value; + if (password !== confirmPassword) { + control.get('confirmPassword').setErrors({ NoPasswordMatch: true }); + } + } + + static patternValidator(regex: RegExp, error: ValidationErrors): ValidationErrors | null { + return (control: AbstractControl): { [key: string]: any } => { + if (!control.value) { + return null; + } + + const valid = regex.test(control.value); + return valid ? null : error; + }; + } +} diff --git a/src/app/_helpers/error.interceptor.spec.ts b/src/app/_helpers/error.interceptor.spec.ts new file mode 100644 index 0000000..4d083ad --- /dev/null +++ b/src/app/_helpers/error.interceptor.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { ErrorInterceptor } from './error.interceptor'; + +describe('ErrorInterceptor', () => { + beforeEach(() => TestBed.configureTestingModule({ + providers: [ + ErrorInterceptor + ] + })); + + it('should be created', () => { + const interceptor: ErrorInterceptor = TestBed.inject(ErrorInterceptor); + expect(interceptor).toBeTruthy(); + }); +}); diff --git a/src/app/_helpers/error.interceptor.ts b/src/app/_helpers/error.interceptor.ts new file mode 100644 index 0000000..4b1511e --- /dev/null +++ b/src/app/_helpers/error.interceptor.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import { + HttpRequest, + HttpHandler, + HttpEvent, + HttpInterceptor +} from '@angular/common/http'; +import {Observable, throwError} from 'rxjs'; +import {catchError} from 'rxjs/operators'; + +@Injectable() +export class ErrorInterceptor implements HttpInterceptor { + + constructor() {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + return next.handle(request).pipe(catchError(err => { + if ([401, 403].indexOf(err.status) !== -1) { + location.reload(true); + } + const error = err.error.message || err.statusText; + return throwError(error); + })); + } +} diff --git a/src/app/_helpers/index.ts b/src/app/_helpers/index.ts new file mode 100644 index 0000000..1c31d75 --- /dev/null +++ b/src/app/_helpers/index.ts @@ -0,0 +1,4 @@ +export * from './custom.validator'; +export * from './error.interceptor'; +export * from './custom-error-state-matcher'; +export * from './unsafe-key-store'; diff --git a/src/app/_helpers/unsafe-key-store.spec.ts b/src/app/_helpers/unsafe-key-store.spec.ts new file mode 100644 index 0000000..56785ce --- /dev/null +++ b/src/app/_helpers/unsafe-key-store.spec.ts @@ -0,0 +1,7 @@ +import { UnsafeKeyStore } from './unsafe-key-store'; + +describe('UnsafeKeyStore', () => { + it('should create an instance', () => { + expect(new UnsafeKeyStore()).toBeTruthy(); + }); +}); diff --git a/src/app/_helpers/unsafe-key-store.ts b/src/app/_helpers/unsafe-key-store.ts new file mode 100644 index 0000000..b4a65d9 --- /dev/null +++ b/src/app/_helpers/unsafe-key-store.ts @@ -0,0 +1,32 @@ +import * as openpgp from '../../assets/js/openpgp.min.js'; + +export function UnsafeKeyStore(): void { + this.key = undefined; +} + +UnsafeKeyStore.prototype.set = async function(privateKeyArmored): Promise { + this.key = (await openpgp.key.readArmored(privateKeyArmored)).keys[0]; + console.log('set pgp key', this.key.getKeyId().toHex()); +}; + +UnsafeKeyStore.prototype.fingerprint = function(): any { + return this.key.keyPacket.fingerprint; +}; + +UnsafeKeyStore.prototype.keyid = function(): any { + return this.key.getKeyId(); +}; + +UnsafeKeyStore.prototype.sign = async function(plainText): Promise { + if (!this.key.isDecrypted()) { + const password = window.prompt('password'); + await this.key.decrypt(password); + } + const opts = { + message: openpgp.message.fromText(plainText), + privateKeys: [this.key], + detached: true, + }; + const signatureObject = await openpgp.sign(opts); + return signatureObject.signature; +};