Skip to content

Commit

Permalink
refactor: convert Hash class to an abstract class
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Dec 10, 2022
1 parent 76b4569 commit 915e644
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 78 deletions.
7 changes: 5 additions & 2 deletions src/drivers/argon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import type argon2 from 'argon2'
import { safeEqual } from '@poppinss/utils'

import { Hash } from '../hash.js'
import { PhcFormatter } from '../phc_formatter.js'
import {
MAX_UINT24,
Expand All @@ -18,7 +19,7 @@ import {
RangeValidator,
randomBytesAsync,
} from '../helpers.js'
import type { ArgonConfig, ArgonVariants, HashDriverContract } from '../types.js'
import type { ArgonConfig, ArgonVariants } from '../types.js'

/**
* Hash driver built on top of "argon2" hash algorigthm. Under the hood
Expand All @@ -34,7 +35,7 @@ import type { ArgonConfig, ArgonVariants, HashDriverContract } from '../types.js
* // $argon2id$v=19$t=3,m=4096,p=1$drxJBWzWahR5tMubp+a1Sw$L/Oh2uw6QKW77i/KQ8eGuOt3ui52hEmmKlu1KBVBxiM
* ```
*/
export class Argon implements HashDriverContract {
export class Argon extends Hash {
/**
* Lazily loaded argon2 binding. Since it is a peer dependency
* we cannot import it at top level
Expand Down Expand Up @@ -66,6 +67,8 @@ export class Argon implements HashDriverContract {
#ids = ['argon2d', 'argon2i', 'argon2id']

constructor(config: ArgonConfig) {
super()

this.#config = {
version: 0x13,
variant: 'id',
Expand Down
8 changes: 6 additions & 2 deletions src/drivers/bcrypt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import * as bcryptBase64 from '../legacy/bcrypt_base64.cjs'

import type bcrypt from 'bcrypt'
import { safeEqual } from '@poppinss/utils'

import { Hash } from '../hash.js'
import type { BcryptConfig } from '../types.js'
import { PhcFormatter } from '../phc_formatter.js'
import type { HashDriverContract, BcryptConfig } from '../types.js'
import { EnumValidator, randomBytesAsync, RangeValidator } from '../helpers.js'

/**
Expand All @@ -29,7 +31,7 @@ import { EnumValidator, randomBytesAsync, RangeValidator } from '../helpers.js'
* // $bcrypt$v=98$r=10$Jtxi46WJ26OQ0khsYLLlnw$knXGfuRFsSjXdj88JydPOnUIglvm1S8
* ```
*/
export class Bcrypt implements HashDriverContract {
export class Bcrypt extends Hash {
/**
* Lazily loaded bcrypt binding. Since it is a peer dependency
* we cannot import it at top level
Expand All @@ -47,6 +49,8 @@ export class Bcrypt implements HashDriverContract {
#phcFormatter = new PhcFormatter<{ r: number }>()

constructor(config: BcryptConfig) {
super()

this.#config = {
rounds: 10,
saltSize: 16,
Expand Down
4 changes: 2 additions & 2 deletions src/drivers/fake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* file that was distributed with this source code.
*/

import type { HashDriverContract } from '../types.js'
import { Hash } from '../hash.js'

/**
* The fake implementation does not generate any hash and
Expand All @@ -16,7 +16,7 @@ import type { HashDriverContract } from '../types.js'
*
* The fake driver is useful for testing.
*/
export class Fake implements HashDriverContract {
export class Fake extends Hash {
/**
* Always returns true
*/
Expand Down
7 changes: 5 additions & 2 deletions src/drivers/scrypt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@

import { safeEqual } from '@poppinss/utils'

import { Hash } from '../hash.js'
import type { ScryptConfig } from '../types.js'
import { PhcFormatter } from '../phc_formatter.js'
import type { ScryptConfig, HashDriverContract } from '../types.js'
import { randomBytesAsync, RangeValidator, scryptAsync, MAX_UINT32 } from '../helpers.js'

/**
Expand All @@ -27,7 +28,7 @@ import { randomBytesAsync, RangeValidator, scryptAsync, MAX_UINT32 } from '../he
* // $scrypt$n=16384,r=8,p=1$iILKD1gVSx6bqualYqyLBQ$DNzIISdmTQS6sFdQ1tJ3UCZ7Uun4uGHNjj0x8FHOqB0pf2LYsu9Xaj5MFhHg21qBz8l5q/oxpeV+ZkgTAj+OzQ
* ```
*/
export class Scrypt implements HashDriverContract {
export class Scrypt extends Hash {
/**
* Config with defaults merged
*/
Expand All @@ -43,6 +44,8 @@ export class Scrypt implements HashDriverContract {
}>()

constructor(config: ScryptConfig) {
super()

this.#config = {
cost: 16384,
blockSize: 8,
Expand Down
62 changes: 32 additions & 30 deletions src/hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,51 @@
import type { HashDriverContract } from './types.js'

/**
* Hash and verify values using a dedicated hash driver. The Hash
* works as an adapter across different drivers.
*
* ```ts
* const hash = new Hash(new Argon())
* const hashedPassword = await hash.make('secret')
*
* const isValid = await hash.verify(hashedPassword, 'secret')
* console.log(isValid)
* ```
* Hash and verify values using a dedicated hash driver. The
* abstract implementation must be extended by the
* implementation drivers
*/
export class Hash implements HashDriverContract {
#driver: HashDriverContract
constructor(driver: HashDriverContract) {
this.#driver = driver
}
export abstract class Hash implements HashDriverContract {
constructor() {}

/**
* Check if the value is a valid hash. This method just checks
* for the formatting of the hash
* for the formatting of the hash.
*/
isValidHash(value: string): boolean {
return this.#driver.isValidHash(value)
}
abstract isValidHash(value: string): boolean

/**
* Hash plain text value
* Hash a plain text value
*
* ```ts
* const hashedValue = await hash.make('password')
* ```
*/
make(value: string): Promise<string> {
return this.#driver.make(value)
}
abstract make(value: string): Promise<string>

/**
* Verify the plain text value against an existing hash
*
* ```ts
* if (await hash.verify(hashedValues, plainText)) {
*
* }
* ```
*/
verify(hashedValue: string, plainValue: string): Promise<boolean> {
return this.#driver.verify(hashedValue, plainValue)
}
abstract verify(hashedValue: string, plainValue: string): Promise<boolean>

/**
* Find if the hash value needs a rehash or not.
* Find if the hash value needs a rehash or not. The rehash is
* required when.
*
* ```ts
* const isValid = await hash.verify(hashedValue, plainText)
*
* // Plain password is valid and hash needs a rehash
* if (isValid && await needs.needsReHash(hashedValue)) {
* const newHashedValue = await hash.make(plainText)
* }
* ```
*/
needsReHash(hashedValue: string): boolean {
return this.#driver.needsReHash(hashedValue)
}
abstract needsReHash(hashedValue: string): boolean
}
4 changes: 2 additions & 2 deletions src/hash_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export class HashManager<KnownHashers extends Record<string, ManagerDriversConfi
* driver and cache it
*/
debug('creating hash driver. name: "%s", config: %O', hasherToUse, config)
const hash = new Hash(this.#createDriver(config.driver, config))
const hash = this.#createDriver(config.driver, config)
this.#hashersCache[config.driver] = hash
return hash
}
Expand All @@ -150,7 +150,7 @@ export class HashManager<KnownHashers extends Record<string, ManagerDriversConfi
debug('enabling fakes')

if (!this.#fakeHasher) {
this.#fakeHasher = new Hash(new Fake())
this.#fakeHasher = new Fake()
}
}

Expand Down
38 changes: 0 additions & 38 deletions tests/hash.spec.ts

This file was deleted.

0 comments on commit 915e644

Please sign in to comment.