/
hash_manager.ts
153 lines (134 loc) · 3.62 KB
/
hash_manager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
* @adonisjs/hash
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { RuntimeException } from '@poppinss/utils'
import debug from './debug.js'
import { Hash } from './hash.js'
import { Fake } from './drivers/fake.js'
import type { HashDriverContract, ManagerDriverFactory } from './types.js'
/**
* HashManager implements the manager/builder pattern to create a use multiple
* hashing algorithm without self managing hash instance.
*
* ```ts
* const manager = new HashManager({
* default: 'argon',
* list: {
* argon: () => new ArgonDriver(),
* bcrypt: () => new BcryptDriver(),
* }
* })
* ```
*/
export class HashManager<KnownHashers extends Record<string, ManagerDriverFactory>>
implements HashDriverContract
{
/**
* Fake hasher
*/
#fakeHasher?: Hash
/**
* Cache of hashers
*/
#hashersCache: Partial<Record<keyof KnownHashers, Hash>> = {}
constructor(public config: { default?: keyof KnownHashers; list: KnownHashers }) {
debug('creating hash manager. config: %O', this.config)
}
/**
* Use one of the registered hashers to hash values.
*
* ```ts
* manager.use() // returns default hasher
* manager.use('argon')
* ```
*/
use<Hasher extends keyof KnownHashers>(hasher?: Hasher): Hash {
let hasherToUse: keyof KnownHashers | undefined = hasher || this.config.default
if (!hasherToUse) {
throw new RuntimeException(
'Cannot create hash instance. No default hasher is defined in the config'
)
}
/**
* Use fake hasher if exists
*/
if (this.#fakeHasher) {
return this.#fakeHasher
}
/**
* Use cached copy if exists
*/
const cachedHasher = this.#hashersCache[hasherToUse]
if (cachedHasher) {
debug('using hasher from cache. name: "%s"', hasherToUse)
return cachedHasher
}
const driverFactory = this.config.list[hasherToUse]
/**
* Create a new instance of Hash class with the selected
* driver and cache it
*/
debug('creating hash driver. name: "%s"', hasherToUse)
const hash = new Hash(driverFactory())
this.#hashersCache[hasherToUse] = hash
return hash
}
/**
* Fake hash drivers to disable hashing values
*/
fake(): void {
debug('enabling fakes')
if (!this.#fakeHasher) {
this.#fakeHasher = new Hash(new Fake())
}
}
/**
* Restore fake
*/
restore() {
debug('restoring fakes')
this.#fakeHasher = undefined
}
/**
* Check if the value is a valid hash. This method just checks
* for the formatting of the hash
*/
isValidHash(value: string): boolean {
return this.use().isValidHash(value)
}
/**
* Hash plain text value
*/
make(value: string): Promise<string> {
return this.use().make(value)
}
/**
* Verify the plain text value against an existing hash
*/
verify(hashedValue: string, plainValue: string): Promise<boolean> {
return this.use().verify(hashedValue, plainValue)
}
/**
* Find if the hash value needs a rehash or not.
*/
needsReHash(hashedValue: string): boolean {
return this.use().needsReHash(hashedValue)
}
/**
* Assert the plain value passes the hash verification
*/
async assertEquals(hashedValue: string, plainValue: string): Promise<void> {
return this.use().assertEquals(hashedValue, plainValue)
}
/**
* Assert the plain value fails the hash verification
*/
async assertNotEquals(hashedValue: string, plainValue: string): Promise<void> {
return this.use().assertNotEquals(hashedValue, plainValue)
}
}