Press n or j to go to the next uncovered block, b, p or k for the previous block.
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 | 63x 63x 63x 63x 63x 63x 63x 159x 159x 159x 5x 5x 4x 4x 1x 3x 3x 3x 11x 11x 11x 3x 19x 19x 16x 14x 3x 2x 11x 28x 28x 8x 8x 20x 20x 32x 32x 32x 32x 32x 63x | import crypto from 'crypto';
import bcrypt from 'bcryptjs';
import { URL } from 'url';
import { Logger } from '../logger';
import UsedTokenError from '../error/used-token-error';
import InvalidTokenError from '../error/invalid-token-error';
import { IUnleashConfig } from '../types/option';
import { IUnleashStores } from '../types/stores';
import {
IResetQuery,
IResetToken,
IResetTokenStore,
} from '../types/stores/reset-token-store';
import { hoursToMilliseconds } from 'date-fns';
interface IInviteLinks {
[key: string]: string;
}
export default class ResetTokenService {
private store: IResetTokenStore;
private logger: Logger;
private readonly unleashBase: string;
constructor(
{ resetTokenStore }: Pick<IUnleashStores, 'resetTokenStore'>,
{ getLogger, server }: Pick<IUnleashConfig, 'getLogger' | 'server'>,
) {
this.store = resetTokenStore;
this.logger = getLogger('/services/reset-token-service.ts');
this.unleashBase = server.unleashUrl;
}
async useAccessToken(token: IResetQuery): Promise<boolean> {
try {
await this.isValid(token.token);
await this.store.useToken(token);
return true;
} catch (e) {
return false;
}
}
async getActiveInvitations(): Promise<IInviteLinks> {
try {
const tokens = await this.store.getActiveTokens();
const links = tokens.reduce((acc, token) => {
const inviteLink =
this.getExistingInvitationUrl(token).toString();
acc[token.userId] = inviteLink;
return acc;
}, {});
return links;
} catch (e) {
return {};
}
}
async isValid(token: string): Promise<IResetToken> {
let t;
try {
t = await this.store.getActive(token);
if (!t.usedAt) {
return t;
}
} catch (e) {
throw new InvalidTokenError();
}
throw new UsedTokenError(t.usedAt);
}
private getExistingInvitationUrl(token: IResetToken) {
return new URL(`${this.unleashBase}/new-user?token=${token.token}`);
}
private async createResetUrl(
forUser: number,
creator: string,
path: string,
): Promise<URL> {
const token = await this.createToken(forUser, creator);
return Promise.resolve(
new URL(`${this.unleashBase}${path}?token=${token.token}`),
);
}
async createResetPasswordUrl(
forUser: number,
creator: string,
): Promise<URL> {
const path = '/reset-password';
return this.createResetUrl(forUser, creator, path);
}
async createNewUserUrl(forUser: number, creator: string): Promise<URL> {
const path = '/new-user';
return this.createResetUrl(forUser, creator, path);
}
async createToken(
tokenUser: number,
creator: string,
expiryDelta: number = hoursToMilliseconds(24),
): Promise<IResetToken> {
const token = await this.generateToken();
const expiry = new Date(Date.now() + expiryDelta);
await this.store.expireExistingTokensForUser(tokenUser);
return this.store.insert({
reset_token: token,
user_id: tokenUser,
expires_at: expiry,
created_by: creator,
});
}
private generateToken(): Promise<string> {
return bcrypt.hash(crypto.randomBytes(32).toString(), 10);
}
}
module.exports = ResetTokenService;
|