@chevre/domain 23.2.0-alpha.41 → 23.2.0-alpha.42

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.
@@ -0,0 +1,14 @@
1
+ // tslint:disable:no-console
2
+ // tslint:disable-next-line:no-require-imports no-var-requires
3
+ const fpe = require('node-fpe-v1');
4
+
5
+ // const orderNumber = 'CIN1-3796505-5174753';
6
+
7
+ const timestamp = '1769837978000';
8
+ const cipher = fpe({ secret: '3:v2-xxxxxx' });
9
+ // .encrypt(orderNumber);
10
+
11
+ const orderNumber = cipher.encrypt(`${timestamp}1`);
12
+ console.log(orderNumber);
13
+ console.log(cipher.decrypt(orderNumber));
14
+ // '1234567'
@@ -0,0 +1,34 @@
1
+ // tslint:disable:no-console
2
+ import * as mongoose from 'mongoose';
3
+ import * as fpe2Module from 'node-fpe-v2';
4
+
5
+ const fpe2 = (fpe2Module as unknown) as typeof fpe2Module.default;
6
+
7
+ import { chevre } from '../../../../lib/index';
8
+
9
+ // tslint:disable-next-line:max-func-body-length
10
+ async function main() {
11
+ await mongoose.connect(<string>process.env.MONGOLAB_URI, { autoIndex: false });
12
+
13
+ const transactionNumberRepo = await chevre.repository.TransactionNumber.createInstance({
14
+ connection: mongoose.connection
15
+ });
16
+
17
+ const result = await transactionNumberRepo.publishByTimestamp({ startDate: new Date() });
18
+ console.log(result);
19
+ console.log('length:', result.transactionNumber.length);
20
+
21
+ const decryptResult = fpe2({ secret: String(result.secret) })
22
+ // tslint:disable-next-line:no-magic-numbers
23
+ .decrypt(result.transactionNumber.slice(2));
24
+ console.log(decryptResult);
25
+ console.log(decryptResult === `${result.timestamp}${result.incrReply.toString()
26
+ // tslint:disable-next-line:no-magic-numbers
27
+ .padStart(2, '0')}`);
28
+ }
29
+
30
+ main()
31
+ .then(() => {
32
+ console.log('success!');
33
+ })
34
+ .catch(console.error);
@@ -0,0 +1,42 @@
1
+ // tslint:disable:object-literal-key-quotes no-console
2
+ import * as mongoose from 'mongoose';
3
+
4
+ import { chevre } from '../../../../lib/index';
5
+
6
+ async function testGeneration() {
7
+ await mongoose.connect(<string>process.env.MONGOLAB_URI, { autoIndex: false });
8
+
9
+ const transactionNumberRepo = await chevre.repository.TransactionNumber.createInstance({
10
+ connection: mongoose.connection
11
+ });
12
+
13
+ const ts = new Date(); // 同一ミリ秒固定
14
+ const results: Record<string, string>[] = [];
15
+
16
+ console.log(`--- 同一ミリ秒(${ts})内での連続生成テスト ---`);
17
+
18
+ // tslint:disable-next-line:no-magic-numbers
19
+ for (let i = 0; i < 100; i += 1) {
20
+ // 同一ミリ秒内でシーケンス(00-99)だけが変わる状況を再現
21
+ const { transactionNumber, timestamp, incrReply } = await transactionNumberRepo.publishByTimestamp({ startDate: ts });
22
+
23
+ // 復号して元のデータに戻るかチェック
24
+ const decoded = await transactionNumberRepo.decrypt(transactionNumber);
25
+ const isSuccess = decoded === `${timestamp}${incrReply.toString()
26
+ // tslint:disable-next-line:no-magic-numbers
27
+ .padStart(2, '0')}`;
28
+
29
+ results.push({
30
+ timestamp: timestamp,
31
+ 'シーケンス': incrReply.toString(),
32
+ '生成された番号 (17桁)': transactionNumber,
33
+ 'ソルト(2文字目)': transactionNumber[1],
34
+ '復号結果': decoded,
35
+ '復号確認': isSuccess ? '✅OK' : '❌NG'
36
+ });
37
+ }
38
+
39
+ console.table(results);
40
+ }
41
+
42
+ testGeneration();
@@ -14,7 +14,7 @@ const cdigit = require("cdigit");
14
14
  const moment = require("moment-timezone");
15
15
  // import { RedisClientType } from 'redis';
16
16
  // tslint:disable-next-line:no-require-imports no-var-requires
17
- const fpe = require('node-fpe');
17
+ const fpe = require('node-fpe-v1');
18
18
  // import { createSchema as createSettingSchema, modelName as settingModelName } from './mongoose/schemas/setting';
19
19
  const transactionNumber_1 = require("./mongoose/schemas/transactionNumber");
20
20
  const transactionNumberCounter_1 = require("./transactionNumberCounter");
@@ -110,6 +110,16 @@ export interface IJWTSetting {
110
110
  */
111
111
  algorithm: Algorithm;
112
112
  }
113
+ export interface ITransactionNumberSetting {
114
+ /**
115
+ * fpe暗号鍵
116
+ */
117
+ fpeSecret: string;
118
+ /**
119
+ * "1"
120
+ */
121
+ version: string;
122
+ }
113
123
  export interface ISetting {
114
124
  defaultSenderEmail?: string;
115
125
  jwt?: IJWTSetting;
@@ -127,6 +137,7 @@ export interface ISetting {
127
137
  };
128
138
  quota?: IQuotaSettings;
129
139
  storage?: IStorageSettings;
140
+ transactionNumber?: ITransactionNumberSetting;
130
141
  /**
131
142
  * 通知設定
132
143
  */
@@ -19,6 +19,7 @@ const schemaDefinition = {
19
19
  onTransactionStatusChanged: mongoose_1.SchemaTypes.Mixed,
20
20
  quota: mongoose_1.SchemaTypes.Mixed,
21
21
  storage: mongoose_1.SchemaTypes.Mixed,
22
+ transactionNumber: mongoose_1.SchemaTypes.Mixed,
22
23
  triggerWebhook: mongoose_1.SchemaTypes.Mixed,
23
24
  useInformResourceTypes: [String],
24
25
  userPoolIdOld: String,
@@ -14,7 +14,7 @@ const cdigit = require("cdigit");
14
14
  const moment = require("moment-timezone");
15
15
  // import { RedisClientType } from 'redis';
16
16
  // tslint:disable-next-line:no-require-imports no-var-requires
17
- const fpe = require('node-fpe');
17
+ const fpe = require('node-fpe-v1');
18
18
  // import { createSchema as createSettingSchema, modelName as settingModelName } from './mongoose/schemas/setting';
19
19
  const transactionNumber_1 = require("./mongoose/schemas/transactionNumber");
20
20
  const transactionNumberCounter_1 = require("./transactionNumberCounter");
@@ -14,7 +14,7 @@ const cdigit = require("cdigit");
14
14
  const moment = require("moment-timezone");
15
15
  // import { RedisClientType } from 'redis';
16
16
  // tslint:disable-next-line:no-require-imports no-var-requires
17
- const fpe = require('node-fpe');
17
+ const fpe = require('node-fpe-v1');
18
18
  // import { createSchema as createSettingSchema, modelName as settingModelName } from './mongoose/schemas/setting';
19
19
  const transactionNumber_1 = require("./mongoose/schemas/transactionNumber");
20
20
  const transactionNumberCounter_1 = require("./transactionNumberCounter");
@@ -1,12 +1,16 @@
1
1
  import type { Connection } from 'mongoose';
2
2
  interface IPublishResult {
3
3
  transactionNumber: string;
4
+ timestamp: string;
5
+ incrReply: number;
6
+ secret?: string;
4
7
  }
5
8
  /**
6
9
  * 取引番号リポジトリ
7
10
  */
8
11
  export declare class TransactionNumberRepo {
9
12
  private readonly counterRepo;
13
+ private readonly settingModel;
10
14
  constructor(params: {
11
15
  connection: Connection;
12
16
  });
@@ -16,5 +20,7 @@ export declare class TransactionNumberRepo {
16
20
  publishByTimestamp(params: {
17
21
  startDate: Date;
18
22
  }): Promise<IPublishResult>;
23
+ decrypt(id: string): Promise<string>;
24
+ private findSetting;
19
25
  }
20
26
  export {};
@@ -11,11 +11,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.TransactionNumberRepo = void 0;
13
13
  const cdigit = require("cdigit");
14
+ const crypto_1 = require("crypto");
14
15
  const moment = require("moment-timezone");
15
- // import { RedisClientType } from 'redis';
16
+ const fpe2Module = require("node-fpe-v2");
17
+ // 型定義が新しいtypescriptに適合しないので、強制的に型変更
18
+ const fpe2 = fpe2Module;
16
19
  // tslint:disable-next-line:no-require-imports no-var-requires
17
- const fpe = require('node-fpe');
18
- // import { createSchema as createSettingSchema, modelName as settingModelName } from './mongoose/schemas/setting';
20
+ const fpe1 = require('node-fpe-v1');
21
+ const factory = require("../factory");
22
+ const setting_1 = require("./mongoose/schemas/setting");
19
23
  const transactionNumber_1 = require("./mongoose/schemas/transactionNumber");
20
24
  const transactionNumberCounter_1 = require("./transactionNumberCounter");
21
25
  /**
@@ -23,25 +27,15 @@ const transactionNumberCounter_1 = require("./transactionNumberCounter");
23
27
  */
24
28
  class TransactionNumberRepo {
25
29
  constructor(params) {
26
- // const { connection } = params;
27
- // this.settingModel = connection.model(settingModelName, createSettingSchema());
28
30
  this.counterRepo = new transactionNumberCounter_1.TransactionNumberCounterRepo(params);
31
+ this.settingModel = params.connection.model(setting_1.modelName, (0, setting_1.createSchema)());
29
32
  }
30
- // private static createKey(params: {
31
- // startDate: Date;
32
- // timestamp: string;
33
- // }): string {
34
- // return util.format(
35
- // '%s:%s',
36
- // TransactionNumberRepo.REDIS_KEY_PREFIX,
37
- // params.timestamp
38
- // );
39
- // }
40
33
  /**
41
34
  * タイムスタンプから発行する
42
35
  */
43
36
  publishByTimestamp(params) {
44
37
  return __awaiter(this, void 0, void 0, function* () {
38
+ const { fpeSecret, version } = yield this.findSetting();
45
39
  const timestamp = moment(params.startDate)
46
40
  .valueOf()
47
41
  .toString();
@@ -57,13 +51,67 @@ class TransactionNumberRepo {
57
51
  includedInDataCatalog: { identifier: transactionNumber_1.DataCatalogIdentifier.transactionNumber },
58
52
  expires: dataFeedExpires
59
53
  });
60
- let transactionNumber = `${timestamp}${incrReply}`;
61
- // checkdigit
62
- const cd = cdigit.luhn.compute(transactionNumber);
63
- transactionNumber = fpe({ password: cd })
64
- .encrypt(transactionNumber);
65
- transactionNumber = `${cd}${transactionNumber}`;
66
- return { transactionNumber };
54
+ let transactionNumber;
55
+ let secret;
56
+ if (typeof fpeSecret === 'string' && typeof version === 'string') {
57
+ // tslint:disable-next-line:no-magic-numbers
58
+ const saltDigit = (0, crypto_1.randomInt)(0, 10);
59
+ const saltStr = saltDigit.toString();
60
+ secret = `${fpeSecret}${saltStr}`;
61
+ const cipher = fpe2({ secret });
62
+ // incrReplyが 0〜99 なら "00"〜"99" になり(15桁)
63
+ // incrReplyが 100〜999 なら "100"〜"999" になる(16桁)
64
+ const rawBody = `${timestamp}${incrReply.toString()
65
+ // tslint:disable-next-line:no-magic-numbers
66
+ .padStart(2, '0')}`;
67
+ const encryptedBody = cipher.encrypt(rawBody);
68
+ // ソルトを「あえて」2文字目に含める
69
+ transactionNumber = `${version}${saltStr}${encryptedBody}`;
70
+ }
71
+ else {
72
+ transactionNumber = `${timestamp}${incrReply}`;
73
+ // checkdigit
74
+ const cd = cdigit.luhn.compute(transactionNumber);
75
+ transactionNumber = fpe1({ password: cd })
76
+ .encrypt(transactionNumber);
77
+ transactionNumber = `${cd}${transactionNumber}`;
78
+ }
79
+ return { transactionNumber, timestamp, incrReply, secret };
80
+ });
81
+ }
82
+ decrypt(id) {
83
+ return __awaiter(this, void 0, void 0, function* () {
84
+ const { fpeSecret } = yield this.findSetting();
85
+ const saltStr = id[1]; // 2文字目からソルト(鍵のヒント)を回収
86
+ // tslint:disable-next-line:no-magic-numbers
87
+ const encryptedBody = id.slice(2);
88
+ const cipher = fpe2({
89
+ secret: `${fpeSecret}${saltStr}`
90
+ });
91
+ return cipher.decrypt(encryptedBody);
92
+ });
93
+ }
94
+ findSetting() {
95
+ return __awaiter(this, void 0, void 0, function* () {
96
+ const filterQuery = { 'project.id': { $eq: '*' } };
97
+ const projection = {
98
+ _id: 0,
99
+ transactionNumber: 1
100
+ };
101
+ const setting = yield this.settingModel.findOne(filterQuery, projection)
102
+ .lean()
103
+ .exec();
104
+ if (setting === null || setting.transactionNumber === undefined) {
105
+ return {};
106
+ }
107
+ const { fpeSecret, version } = setting.transactionNumber;
108
+ if (typeof fpeSecret !== 'string' || fpeSecret === '') {
109
+ throw new factory.errors.NotFound('setting.transactionNumber.secret');
110
+ }
111
+ if (typeof version !== 'string' || version === '') {
112
+ throw new factory.errors.NotFound('setting.transactionNumber.version');
113
+ }
114
+ return { fpeSecret, version };
67
115
  });
68
116
  }
69
117
  }
package/package.json CHANGED
@@ -25,7 +25,8 @@
25
25
  "lodash.difference": "^4.5.0",
26
26
  "moment": "^2.29.1",
27
27
  "moment-timezone": "^0.5.33",
28
- "node-fpe": "1.0.0",
28
+ "node-fpe-v1": "npm:node-fpe@1.0.0",
29
+ "node-fpe-v2": "npm:node-fpe@2.0.4",
29
30
  "pug": "^2.0.4",
30
31
  "uniqid": "5.4.0",
31
32
  "uuid": "^3.4.0"
@@ -116,5 +117,5 @@
116
117
  "postversion": "git push origin --tags",
117
118
  "prepublishOnly": "npm run clean && npm run build && npm test && npm run doc"
118
119
  },
119
- "version": "23.2.0-alpha.41"
120
+ "version": "23.2.0-alpha.42"
120
121
  }