password-service 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # password
package/lib/index.js ADDED
@@ -0,0 +1,335 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (_) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __spreadArrays = (this && this.__spreadArrays) || function () {
39
+ for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
40
+ for (var r = Array(s), k = 0, i = 0; i < il; i++)
41
+ for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
42
+ r[k] = a[j];
43
+ return r;
44
+ };
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ var util = require("util");
47
+ var PasswordService = (function () {
48
+ function PasswordService(comparator, repository, sendResetCode, expires, resetCodeRepository, duplicateCount, revokeTokens, hasTwoFactors, gen, sendPasscode, passwordChangeExpires, passcodeRepository) {
49
+ this.comparator = comparator;
50
+ this.repository = repository;
51
+ this.sendResetCode = sendResetCode;
52
+ this.expires = expires;
53
+ this.resetCodeRepository = resetCodeRepository;
54
+ this.revokeTokens = revokeTokens;
55
+ this.hasTwoFactors = hasTwoFactors;
56
+ this.sendPasscode = sendPasscode;
57
+ this.passwordChangeExpires = passwordChangeExpires;
58
+ this.passcodeRepository = passcodeRepository;
59
+ this.generate = (gen ? gen : generate);
60
+ this.duplicateCount = (duplicateCount !== undefined ? duplicateCount : 0);
61
+ this.change = this.change.bind(this);
62
+ this.forgot = this.forgot.bind(this);
63
+ this.reset = this.reset.bind(this);
64
+ this.duplicate = this.duplicate.bind(this);
65
+ }
66
+ PasswordService.prototype.change = function (pass) {
67
+ return __awaiter(this, void 0, void 0, function () {
68
+ var user, valid, histories, isDuplicate, required, repo, sentCode, savedCode, expires, expiredTime, res0, send, code, validPasscode, newPassword, res;
69
+ return __generator(this, function (_a) {
70
+ switch (_a.label) {
71
+ case 0:
72
+ if (pass.step && pass.step > 0 && (!pass.passcode || pass.passcode.length === 0)) {
73
+ return [2, 0];
74
+ }
75
+ return [4, this.repository.getUser(pass.username)];
76
+ case 1:
77
+ user = _a.sent();
78
+ if (!user) {
79
+ return [2, 0];
80
+ }
81
+ if (!user.id) {
82
+ return [2, 0];
83
+ }
84
+ return [4, this.comparator.compare(pass.currentPassword, user.password)];
85
+ case 2:
86
+ valid = _a.sent();
87
+ if (!valid) {
88
+ return [2, 0];
89
+ }
90
+ if (!(this.duplicateCount > 0)) return [3, 5];
91
+ return [4, this.repository.getHistory(user.id, this.duplicateCount - 1)];
92
+ case 3:
93
+ histories = _a.sent();
94
+ return [4, this.duplicate(pass.password, this.duplicateCount, user.password, histories)];
95
+ case 4:
96
+ isDuplicate = _a.sent();
97
+ if (isDuplicate) {
98
+ return [2, -1];
99
+ }
100
+ _a.label = 5;
101
+ case 5:
102
+ if (!this.hasTwoFactors) return [3, 15];
103
+ return [4, this.hasTwoFactors(user.id)];
104
+ case 6:
105
+ required = _a.sent();
106
+ if (!required) return [3, 15];
107
+ if (!pass.passcode || pass.passcode.length === 0) {
108
+ return [2, 0];
109
+ }
110
+ repo = (this.passcodeRepository ? this.passcodeRepository : this.resetCodeRepository);
111
+ if (!(!pass.step || pass.step <= 0)) return [3, 11];
112
+ sentCode = this.generate();
113
+ return [4, this.comparator.hash(sentCode)];
114
+ case 7:
115
+ savedCode = _a.sent();
116
+ expires = (this.passwordChangeExpires ? this.passwordChangeExpires : this.expires);
117
+ expiredTime = addSeconds(new Date(), expires);
118
+ return [4, repo.save(user.id, savedCode, expiredTime)];
119
+ case 8:
120
+ res0 = _a.sent();
121
+ if (!(res0 > 0)) return [3, 10];
122
+ send = (this.sendPasscode ? this.sendPasscode : this.sendResetCode);
123
+ return [4, send(user.contact, sentCode, expiredTime, user.username)];
124
+ case 9:
125
+ _a.sent();
126
+ return [2, 2];
127
+ case 10: return [3, 15];
128
+ case 11: return [4, repo.load(user.id)];
129
+ case 12:
130
+ code = _a.sent();
131
+ if (!code) {
132
+ return [2, 0];
133
+ }
134
+ if (after(new Date(), code.expiredAt)) {
135
+ return [2, 0];
136
+ }
137
+ return [4, this.comparator.compare(pass.passcode, code.code)];
138
+ case 13:
139
+ validPasscode = _a.sent();
140
+ if (!!validPasscode) return [3, 15];
141
+ return [4, repo.delete(user.id)];
142
+ case 14:
143
+ _a.sent();
144
+ return [2, 0];
145
+ case 15: return [4, this.comparator.hash(pass.password)];
146
+ case 16:
147
+ newPassword = _a.sent();
148
+ return [4, this.repository.update(user.id, newPassword, user.password)];
149
+ case 17:
150
+ res = _a.sent();
151
+ if (!(res > 0)) return [3, 20];
152
+ if (!this.revokeTokens) return [3, 19];
153
+ return [4, this.revokeTokens('' + user.id, 'The user has changed password.')];
154
+ case 18:
155
+ _a.sent();
156
+ _a.label = 19;
157
+ case 19: return [2, 1];
158
+ case 20: return [2, 0];
159
+ }
160
+ });
161
+ });
162
+ };
163
+ PasswordService.prototype.forgot = function (contact) {
164
+ var _this = this;
165
+ return this.repository.getUser(contact).then(function (user) {
166
+ if (!user) {
167
+ return false;
168
+ }
169
+ else {
170
+ var sentCode_1 = _this.generate();
171
+ return _this.comparator.hash(sentCode_1).then(function (savedCode) {
172
+ var expiredAt = addSeconds(new Date(), _this.expires);
173
+ return _this.resetCodeRepository.save(user.id, savedCode, expiredAt).then(function (res) {
174
+ if (res > 0) {
175
+ return _this.sendResetCode(user.contact, sentCode_1, expiredAt, user.username);
176
+ }
177
+ else {
178
+ return false;
179
+ }
180
+ });
181
+ });
182
+ }
183
+ });
184
+ };
185
+ PasswordService.prototype.reset = function (pass) {
186
+ return __awaiter(this, void 0, void 0, function () {
187
+ var excludePassword, user, code, valid, newPassword, histories, isDuplicate, oldPassword, res;
188
+ return __generator(this, function (_a) {
189
+ switch (_a.label) {
190
+ case 0:
191
+ excludePassword = (this.duplicateCount <= 0);
192
+ return [4, this.repository.getUser(pass.username, excludePassword)];
193
+ case 1:
194
+ user = _a.sent();
195
+ if (!user) {
196
+ return [2, 0];
197
+ }
198
+ return [4, this.resetCodeRepository.load(user.id)];
199
+ case 2:
200
+ code = _a.sent();
201
+ if (!code) {
202
+ return [2, 0];
203
+ }
204
+ if (!after(new Date(), code.expiredAt)) return [3, 4];
205
+ return [4, this.resetCodeRepository.delete(user.id)];
206
+ case 3:
207
+ _a.sent();
208
+ return [2, 0];
209
+ case 4: return [4, this.comparator.compare(pass.passcode, code.code)];
210
+ case 5:
211
+ valid = _a.sent();
212
+ if (!valid) {
213
+ return [2, 0];
214
+ }
215
+ return [4, this.comparator.hash(pass.password)];
216
+ case 6:
217
+ newPassword = _a.sent();
218
+ if (!(this.duplicateCount > 0)) return [3, 9];
219
+ return [4, this.repository.getHistory(user.id, this.duplicateCount - 1)];
220
+ case 7:
221
+ histories = _a.sent();
222
+ return [4, this.duplicate(pass.password, this.duplicateCount, user.password, histories)];
223
+ case 8:
224
+ isDuplicate = _a.sent();
225
+ if (isDuplicate) {
226
+ return [2, -1];
227
+ }
228
+ _a.label = 9;
229
+ case 9:
230
+ oldPassword = (this.duplicateCount > 0 ? user.password : undefined);
231
+ return [4, this.repository.update(user.id, newPassword, oldPassword)];
232
+ case 10:
233
+ res = _a.sent();
234
+ if (!(res > 0)) return [3, 13];
235
+ if (!this.revokeTokens) return [3, 12];
236
+ return [4, this.revokeTokens('' + user.id, 'The user has reset password.')];
237
+ case 11:
238
+ _a.sent();
239
+ _a.label = 12;
240
+ case 12: return [2, 1];
241
+ case 13: return [2, 0];
242
+ }
243
+ });
244
+ });
245
+ };
246
+ PasswordService.prototype.duplicate = function (newPassword, count, currentPassword, histories) {
247
+ return __awaiter(this, void 0, void 0, function () {
248
+ var equal, length, l, i, equal;
249
+ return __generator(this, function (_a) {
250
+ switch (_a.label) {
251
+ case 0:
252
+ if (!(currentPassword && currentPassword.length > 0)) return [3, 2];
253
+ return [4, this.comparator.compare(newPassword, currentPassword)];
254
+ case 1:
255
+ equal = _a.sent();
256
+ if (equal) {
257
+ return [2, equal];
258
+ }
259
+ _a.label = 2;
260
+ case 2:
261
+ if (!(histories && histories.length > 0)) return [3, 6];
262
+ length = Math.min(count - 2, histories.length);
263
+ l = histories.length;
264
+ i = 1;
265
+ _a.label = 3;
266
+ case 3:
267
+ if (!(i <= length)) return [3, 6];
268
+ return [4, this.comparator.compare(newPassword, histories[l - i])];
269
+ case 4:
270
+ equal = _a.sent();
271
+ if (equal) {
272
+ return [2, equal];
273
+ }
274
+ _a.label = 5;
275
+ case 5:
276
+ i++;
277
+ return [3, 3];
278
+ case 6: return [2, false];
279
+ }
280
+ });
281
+ });
282
+ };
283
+ return PasswordService;
284
+ }());
285
+ exports.PasswordService = PasswordService;
286
+ function addSeconds(date, seconds) {
287
+ var d = new Date(date);
288
+ d.setSeconds(d.getSeconds() + seconds);
289
+ return d;
290
+ }
291
+ exports.addSeconds = addSeconds;
292
+ function after(d1, d2) {
293
+ return (d1.getTime() - d2.getTime() > 0);
294
+ }
295
+ exports.after = after;
296
+ function generate(length) {
297
+ if (!length) {
298
+ length = 6;
299
+ }
300
+ return padLeft(Math.floor(Math.random() * Math.floor(Math.pow(10, length) - 1)).toString(), length, '0');
301
+ }
302
+ exports.generate = generate;
303
+ function padLeft(str, length, pad) {
304
+ if (str.length >= length) {
305
+ return str;
306
+ }
307
+ var str2 = str;
308
+ while (str2.length < length) {
309
+ str2 = pad + str2;
310
+ }
311
+ return str2;
312
+ }
313
+ exports.padLeft = padLeft;
314
+ var MailSender = (function () {
315
+ function MailSender(sendMail, from, body, subject) {
316
+ this.sendMail = sendMail;
317
+ this.from = from;
318
+ this.body = body;
319
+ this.subject = subject;
320
+ this.send = this.send.bind(this);
321
+ }
322
+ MailSender.prototype.send = function (to, passcode, expireAt, params) {
323
+ var diff = Math.abs(Math.round(((Date.now() - expireAt.getTime()) / 1000) / 60));
324
+ var body = util.format.apply(util, __spreadArrays([this.body], [params, passcode, diff, params, passcode, diff]));
325
+ var msg = {
326
+ to: to,
327
+ from: this.from,
328
+ subject: this.subject,
329
+ html: body
330
+ };
331
+ return this.sendMail(msg);
332
+ };
333
+ return MailSender;
334
+ }());
335
+ exports.MailSender = MailSender;
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "password-service",
3
+ "version": "0.0.1",
4
+ "description": "password",
5
+ "main": "./lib/index.js",
6
+ "types": "./src/index.ts",
7
+ "scripts": {
8
+ "build:lib": "tsc",
9
+ "clean:lib": "rimraf lib"
10
+ },
11
+ "devDependencies": {
12
+ "tslint": "5.10.0",
13
+ "typescript": "^3.8.3"
14
+ },
15
+ "publishConfig": {
16
+ "registry": "https://registry.npmjs.org/"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git@github.com/core-ts/password"
21
+ },
22
+ "keywords": [
23
+ "password"
24
+ ]
25
+ }
package/src/index.ts ADDED
@@ -0,0 +1,308 @@
1
+ import * as util from 'util';
2
+
3
+ export interface Collections {
4
+ user: string;
5
+ password: string;
6
+ history: string;
7
+ }
8
+ export interface CollectionsConfig {
9
+ user: string;
10
+ password?: string;
11
+ history?: string;
12
+ }
13
+ export interface FieldConfig {
14
+ contact?: string;
15
+ username?: string;
16
+ password?: string;
17
+ history?: string;
18
+ changedTime?: string;
19
+ failCount?: string;
20
+ }
21
+ export interface Template {
22
+ subject: string;
23
+ body: string;
24
+ }
25
+ export interface PasswordConfig {
26
+ max?: number;
27
+ expires: number;
28
+ fields?: FieldConfig;
29
+ db: CollectionsConfig;
30
+ }
31
+ export interface PasswordTemplateConfig extends PasswordConfig {
32
+ templates: {
33
+ reset: Template;
34
+ change: Template;
35
+ };
36
+ }
37
+ export interface PasswordReset {
38
+ username: string;
39
+ passcode: string;
40
+ password: string;
41
+ }
42
+ export interface PasswordChange {
43
+ step?: number;
44
+ username: string;
45
+ passcode?: string;
46
+ currentPassword: string;
47
+ password: string;
48
+ }
49
+ export interface Comparator {
50
+ compare(data: string, encrypted: string): Promise<boolean>;
51
+ hash(plaintext: string): Promise<string>;
52
+ }
53
+ export interface Passcode {
54
+ expiredAt: Date;
55
+ code: string;
56
+ }
57
+ export interface PasscodeRepository<ID> {
58
+ save(id: ID, passcode: string, expireAt: Date): Promise<number>;
59
+ load(id: ID): Promise<Passcode>;
60
+ delete(id: ID): Promise<number>;
61
+ }
62
+ export interface User<ID> {
63
+ id: ID;
64
+ username: string;
65
+ password: string;
66
+ contact: string;
67
+ }
68
+ export interface PasswordRepository<ID> {
69
+ getUser(userNameOrEmail: string, exludePassword?: boolean): Promise<User<ID>>;
70
+ update(userId: ID, newPassword: string, oldPassword?: string): Promise<number>;
71
+ getHistory(userId: ID, max?: number): Promise<string[]>;
72
+ }
73
+ /*
74
+ export interface PasswordService {
75
+ forgot(email: string): Promise<boolean>;
76
+ reset(pass: PasswordReset): Promise<number>;
77
+ change(pass: PasswordChange): Promise<number>;
78
+ }
79
+ */
80
+ export class PasswordService<ID> {
81
+ generate: () => string;
82
+ duplicateCount: number;
83
+ constructor(
84
+ public comparator: Comparator,
85
+ public repository: PasswordRepository<ID>,
86
+ public sendResetCode: (to: string, passcode: string, expireAt: Date, params?: any) => Promise<boolean>,
87
+ public expires: number,
88
+ public resetCodeRepository: PasscodeRepository<ID>,
89
+ duplicateCount?: number,
90
+ public revokeTokens?: (id: string, reason?: string) => Promise<boolean>,
91
+ public hasTwoFactors?: (id: ID) => Promise<boolean>,
92
+ gen?: () => string,
93
+ public sendPasscode?: (to: string, passcode: string, expireAt: Date, params?: any) => Promise<boolean>,
94
+ public passwordChangeExpires?: number,
95
+ public passcodeRepository?: PasscodeRepository<ID>
96
+ ) {
97
+ this.generate = (gen ? gen : generate);
98
+ this.duplicateCount = (duplicateCount !== undefined ? duplicateCount : 0);
99
+ this.change = this.change.bind(this);
100
+ this.forgot = this.forgot.bind(this);
101
+ this.reset = this.reset.bind(this);
102
+ this.duplicate = this.duplicate.bind(this);
103
+ }
104
+
105
+ async change(pass: PasswordChange): Promise<number> {
106
+ if (pass.step && pass.step > 0 && (!pass.passcode || pass.passcode.length === 0)) {
107
+ return 0;
108
+ }
109
+ const user = await this.repository.getUser(pass.username);
110
+ if (!user) {
111
+ return 0;
112
+ }
113
+ if (!user.id) {
114
+ return 0;
115
+ }
116
+ const valid = await this.comparator.compare(pass.currentPassword, user.password);
117
+ if (!valid) {
118
+ return 0;
119
+ }
120
+ if (this.duplicateCount > 0) {
121
+ const histories = await this.repository.getHistory(user.id, this.duplicateCount - 1);
122
+ const isDuplicate = await this.duplicate(pass.password, this.duplicateCount, user.password, histories);
123
+ if (isDuplicate) {
124
+ return -1;
125
+ }
126
+ }
127
+ if (this.hasTwoFactors) {
128
+ const required = await this.hasTwoFactors(user.id);
129
+ if (required) {
130
+ if (!pass.passcode || pass.passcode.length === 0) {
131
+ return 0;
132
+ }
133
+ const repo = (this.passcodeRepository ? this.passcodeRepository : this.resetCodeRepository);
134
+ if (!pass.step || pass.step <= 0) {
135
+ const sentCode = this.generate();
136
+ const savedCode = await this.comparator.hash(sentCode);
137
+ const expires = (this.passwordChangeExpires ? this.passwordChangeExpires : this.expires);
138
+ const expiredTime = addSeconds(new Date(), expires);
139
+ const res0 = await repo.save(user.id, savedCode, expiredTime);
140
+ if (res0 > 0) {
141
+ const send = (this.sendPasscode ? this.sendPasscode : this.sendResetCode);
142
+ await send(user.contact, sentCode, expiredTime, user.username);
143
+ return 2;
144
+ }
145
+ } else {
146
+ const code = await repo.load(user.id);
147
+ if (!code) {
148
+ return 0;
149
+ }
150
+ if (after(new Date(), code.expiredAt)) {
151
+ return 0;
152
+ }
153
+ const validPasscode = await this.comparator.compare(pass.passcode, code.code);
154
+ if (!validPasscode) {
155
+ await repo.delete(user.id);
156
+ return 0;
157
+ }
158
+ }
159
+ }
160
+ }
161
+ const newPassword = await this.comparator.hash(pass.password);
162
+ const res = await this.repository.update(user.id, newPassword, user.password);
163
+ if (res > 0) {
164
+ if (this.revokeTokens) {
165
+ await this.revokeTokens('' + user.id, 'The user has changed password.');
166
+ }
167
+ return 1;
168
+ } else {
169
+ return 0;
170
+ }
171
+ }
172
+ forgot(contact: string): Promise<boolean> {
173
+ return this.repository.getUser(contact).then(user => {
174
+ if (!user) {
175
+ return false;
176
+ } else {
177
+ const sentCode = this.generate();
178
+ return this.comparator.hash(sentCode).then(savedCode => {
179
+ const expiredAt = addSeconds(new Date(), this.expires);
180
+ return this.resetCodeRepository.save(user.id, savedCode, expiredAt).then(res => {
181
+ if (res > 0) {
182
+ return this.sendResetCode(user.contact, sentCode, expiredAt, user.username);
183
+ } else {
184
+ return false;
185
+ }
186
+ });
187
+ });
188
+ }
189
+ });
190
+ }
191
+ async reset(pass: PasswordReset): Promise<number> {
192
+ const excludePassword = (this.duplicateCount <= 0);
193
+ const user = await this.repository.getUser(pass.username, excludePassword);
194
+ if (!user) {
195
+ return 0;
196
+ }
197
+ const code = await this.resetCodeRepository.load(user.id);
198
+ if (!code) {
199
+ return 0;
200
+ }
201
+ if (after(new Date(), code.expiredAt)) {
202
+ await this.resetCodeRepository.delete(user.id);
203
+ return 0;
204
+ }
205
+ const valid = await this.comparator.compare(pass.passcode, code.code);
206
+ if (!valid) {
207
+ return 0;
208
+ }
209
+ const newPassword = await this.comparator.hash(pass.password);
210
+ if (this.duplicateCount > 0) {
211
+ const histories = await this.repository.getHistory(user.id, this.duplicateCount - 1);
212
+ const isDuplicate = await this.duplicate(pass.password, this.duplicateCount, user.password, histories);
213
+ if (isDuplicate) {
214
+ return -1;
215
+ }
216
+ }
217
+ const oldPassword = (this.duplicateCount > 0 ? user.password : undefined);
218
+ const res = await this.repository.update(user.id, newPassword, oldPassword);
219
+ if (res > 0) {
220
+ if (this.revokeTokens) {
221
+ await this.revokeTokens('' + user.id, 'The user has reset password.');
222
+ }
223
+ return 1;
224
+ } else {
225
+ return 0;
226
+ }
227
+ }
228
+ private async duplicate(newPassword: string, count: number, currentPassword: string, histories: string[]): Promise<boolean> {
229
+ if (currentPassword && currentPassword.length > 0) {
230
+ const equal = await this.comparator.compare(newPassword, currentPassword);
231
+ if (equal) {
232
+ return equal;
233
+ }
234
+ }
235
+ if (histories && histories.length > 0) {
236
+ const length = Math.min(count - 2, histories.length);
237
+ const l = histories.length;
238
+ for (let i = 1; i <= length; i++) {
239
+ const equal = await this.comparator.compare(newPassword, histories[l - i]);
240
+ if (equal) {
241
+ return equal;
242
+ }
243
+ }
244
+ }
245
+ return false;
246
+ }
247
+ }
248
+ export function addSeconds(date: Date, seconds: number) {
249
+ const d = new Date(date);
250
+ d.setSeconds(d.getSeconds() + seconds);
251
+ return d;
252
+ }
253
+ export function after(d1: Date, d2: Date): boolean {
254
+ return (d1.getTime() - d2.getTime() > 0);
255
+ }
256
+ export function generate(length?: number): string {
257
+ if (!length) {
258
+ length = 6;
259
+ }
260
+ return padLeft(Math.floor(Math.random() * Math.floor(Math.pow(10, length) - 1)).toString(), length, '0');
261
+ }
262
+ export function padLeft(str: string, length: number, pad: string) {
263
+ if (str.length >= length) {
264
+ return str;
265
+ }
266
+ let str2 = str;
267
+ while (str2.length < length) {
268
+ str2 = pad + str2;
269
+ }
270
+ return str2;
271
+ }
272
+ export type EmailData = string|{ name?: string; email: string; };
273
+ export interface MailContent {
274
+ type: string;
275
+ value: string;
276
+ }
277
+ export interface MailData {
278
+ to?: EmailData|EmailData[];
279
+
280
+ from: EmailData;
281
+ replyTo?: EmailData;
282
+
283
+ subject?: string;
284
+ html?: string;
285
+ content?: MailContent[];
286
+ }
287
+ // tslint:disable-next-line:max-classes-per-file
288
+ export class MailSender {
289
+ constructor(
290
+ public sendMail: (mailData: MailData) => Promise<boolean>,
291
+ public from: EmailData,
292
+ public body: string,
293
+ public subject: string
294
+ ) {
295
+ this.send = this.send.bind(this);
296
+ }
297
+ send(to: string, passcode: string, expireAt: Date, params?: any): Promise<boolean> {
298
+ const diff = Math.abs(Math.round(((Date.now() - expireAt.getTime()) / 1000) / 60));
299
+ const body = util.format(this.body, ...[params as string, passcode, diff, params, passcode, diff]);
300
+ const msg = {
301
+ to,
302
+ from: this.from,
303
+ subject: this.subject,
304
+ html: body
305
+ };
306
+ return this.sendMail(msg);
307
+ }
308
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compileOnSave": true,
3
+ "compilerOptions": {
4
+ "target": "es5",
5
+ "baseUrl": "./src",
6
+ "outDir": "./lib",
7
+ "module": "commonjs",
8
+ "moduleResolution": "node",
9
+ "strict": true,
10
+ "pretty": true,
11
+ "sourceMap": false,
12
+ "declaration": false,
13
+ "experimentalDecorators": false,
14
+ "removeComments": true,
15
+ "lib": [
16
+ "es5",
17
+ "es6"
18
+ ]
19
+ },
20
+ "include": [
21
+ "src/**/*.ts"
22
+ ],
23
+ "exclude": [
24
+ "node_modules"
25
+ ]
26
+ }