-
-
Notifications
You must be signed in to change notification settings - Fork 768
/
Copy pathreset-token-store.ts
137 lines (116 loc) · 3.99 KB
/
reset-token-store.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
import type { EventEmitter } from 'events';
import metricsHelper from '../util/metrics-helper';
import { DB_TIME } from '../metric-events';
import type { Logger, LogProvider } from '../logger';
import NotFoundError from '../error/notfound-error';
import type {
IResetQuery,
IResetToken,
IResetTokenCreate,
IResetTokenQuery,
IResetTokenStore,
} from '../types/stores/reset-token-store';
import type { Db } from './db';
const TABLE = 'reset_tokens';
interface IResetTokenTable {
reset_token: string;
user_id: number;
expires_at: Date;
created_at: Date;
created_by: string;
used_at: Date;
}
const rowToResetToken = (row: IResetTokenTable): IResetToken => ({
userId: row.user_id,
token: row.reset_token,
expiresAt: row.expires_at,
createdAt: row.created_at,
createdBy: row.created_by,
usedAt: row.used_at,
});
export class ResetTokenStore implements IResetTokenStore {
private logger: Logger;
private timer: Function;
private db: Db;
constructor(db: Db, eventBus: EventEmitter, getLogger: LogProvider) {
this.db = db;
this.logger = getLogger('db/reset-token-store.ts');
this.timer = (action: string) =>
metricsHelper.wrapTimer(eventBus, DB_TIME, {
store: 'reset-tokens',
action,
});
}
async getActive(token: string): Promise<IResetToken> {
const row = await this.db<IResetTokenTable>(TABLE)
.where({ reset_token: token })
.where('expires_at', '>', new Date())
.first();
if (!row) {
throw new NotFoundError('Could not find an active token');
}
return rowToResetToken(row);
}
async getActiveTokens(): Promise<IResetToken[]> {
const rows = await this.db<IResetTokenTable>(TABLE)
.whereNull('used_at')
.andWhere('expires_at', '>', new Date());
return rows.map(rowToResetToken);
}
async insert(newToken: IResetTokenCreate): Promise<IResetToken> {
const [row] = await this.db<IResetTokenTable>(TABLE)
.insert(newToken)
.returning(['created_at']);
return {
userId: newToken.user_id,
token: newToken.reset_token,
expiresAt: newToken.expires_at,
createdAt: row.created_at,
createdBy: newToken.created_by,
};
}
async useToken(token: IResetQuery): Promise<boolean> {
try {
await this.db<IResetTokenTable>(TABLE)
.update({ used_at: new Date() })
.where({ reset_token: token.token, user_id: token.userId });
return true;
} catch (e) {
return false;
}
}
async deleteFromQuery({ reset_token }: IResetTokenQuery): Promise<void> {
return this.db(TABLE).where(reset_token).del();
}
async deleteAll(): Promise<void> {
return this.db(TABLE).del();
}
async deleteExpired(): Promise<void> {
return this.db(TABLE).where('expires_at', '<', new Date()).del();
}
async expireExistingTokensForUser(user_id: number): Promise<void> {
await this.db<IResetTokenTable>(TABLE).where({ user_id }).update({
expires_at: new Date(),
});
}
async delete(reset_token: string): Promise<void> {
await this.db(TABLE).where({ reset_token }).del();
}
destroy(): void {}
async exists(reset_token: string): Promise<boolean> {
const result = await this.db.raw(
`SELECT EXISTS (SELECT 1 FROM ${TABLE} WHERE reset_token = ?) AS present`,
[reset_token],
);
const { present } = result.rows[0];
return present;
}
async get(key: string): Promise<IResetToken> {
const row = await this.db(TABLE).where({ reset_token: key }).first();
return rowToResetToken(row);
}
async getAll(): Promise<IResetToken[]> {
const rows = await this.db(TABLE).select();
return rows.map(rowToResetToken);
}
}