@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.
- package/example/src/chevre/orderNumber/decryptOrderNumber.ts +14 -0
- package/example/src/chevre/transactionNumber/publishByTimestamp.ts +34 -0
- package/example/src/chevre/transactionNumber/testRandomness.ts +42 -0
- package/lib/chevre/repo/confirmationNumber.js +1 -1
- package/lib/chevre/repo/mongoose/schemas/setting.d.ts +11 -0
- package/lib/chevre/repo/mongoose/schemas/setting.js +1 -0
- package/lib/chevre/repo/orderNumber.js +1 -1
- package/lib/chevre/repo/serviceOutputIdentifier.js +1 -1
- package/lib/chevre/repo/transactionNumber.d.ts +6 -0
- package/lib/chevre/repo/transactionNumber.js +70 -22
- package/package.json +3 -2
|
@@ -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
|
-
|
|
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
|
|
18
|
-
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
.
|
|
65
|
-
|
|
66
|
-
|
|
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.
|
|
120
|
+
"version": "23.2.0-alpha.42"
|
|
120
121
|
}
|