@opengovsg/formsg-sdk 7.1.4 → 7.2.0
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/dist/cjs/adapt-v3-to-v4.d.ts +10 -0
- package/dist/cjs/adapt-v3-to-v4.js +139 -0
- package/dist/cjs/adapt-v4-to-v3.d.ts +9 -0
- package/dist/cjs/adapt-v4-to-v3.js +132 -0
- package/dist/cjs/adapters.d.ts +3 -0
- package/dist/cjs/adapters.js +9 -0
- package/dist/cjs/constants-v4.d.ts +3 -0
- package/dist/cjs/constants-v4.js +27 -0
- package/dist/cjs/crypto-v3.d.ts +17 -0
- package/dist/cjs/crypto-v3.js +38 -0
- package/dist/cjs/index.d.ts +6 -1
- package/dist/cjs/index.js +10 -0
- package/dist/cjs/types-v4.d.ts +148 -0
- package/dist/cjs/types-v4.js +2 -0
- package/dist/cjs/util/crypto.d.ts +6 -0
- package/dist/cjs/util/crypto.js +13 -1
- package/dist/esm/adapt-v3-to-v4.d.ts +10 -0
- package/dist/esm/adapt-v3-to-v4.js +136 -0
- package/dist/esm/adapt-v4-to-v3.d.ts +9 -0
- package/dist/esm/adapt-v4-to-v3.js +129 -0
- package/dist/esm/adapters.d.ts +3 -0
- package/dist/esm/adapters.js +3 -0
- package/dist/esm/constants-v4.d.ts +3 -0
- package/dist/esm/constants-v4.js +24 -0
- package/dist/esm/crypto-v3.d.ts +17 -0
- package/dist/esm/crypto-v3.js +37 -0
- package/dist/esm/index.d.ts +6 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/types-v4.d.ts +148 -0
- package/dist/esm/types-v4.js +1 -0
- package/dist/esm/util/crypto.d.ts +6 -0
- package/dist/esm/util/crypto.js +11 -0
- package/package.json +14 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FormFieldsV3 } from './types';
|
|
2
|
+
import { AdaptV3ToV4Options, FieldResponsesV4 } from './types-v4';
|
|
3
|
+
/**
|
|
4
|
+
* Converts V3 decrypted responses to V4 normalized shape.
|
|
5
|
+
*
|
|
6
|
+
* @param v3Responses - The V3 responses record (field ID → { fieldType, answer })
|
|
7
|
+
* @param options - Optional provenance metadata to stamp on each response
|
|
8
|
+
* @returns V4 responses record
|
|
9
|
+
*/
|
|
10
|
+
export declare function adaptV3ToV4(v3Responses: FormFieldsV3, options?: AdaptV3ToV4Options): FieldResponsesV4;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.adaptV3ToV4 = adaptV3ToV4;
|
|
15
|
+
var constants_v4_1 = require("./constants-v4");
|
|
16
|
+
var crypto_1 = require("./util/crypto");
|
|
17
|
+
var convertStringAnswer = function (answer) {
|
|
18
|
+
return { value: answer };
|
|
19
|
+
};
|
|
20
|
+
var convertYesNoAnswer = function (answer) {
|
|
21
|
+
if (answer !== 'Yes' && answer !== 'No') {
|
|
22
|
+
throw new Error("Invalid yes_no answer: ".concat(answer));
|
|
23
|
+
}
|
|
24
|
+
return { value: answer };
|
|
25
|
+
};
|
|
26
|
+
var convertVerifiableAnswer = function (answer) {
|
|
27
|
+
return __assign({ value: answer.value }, (answer.signature !== undefined && { signature: answer.signature }));
|
|
28
|
+
};
|
|
29
|
+
var convertRadioAnswer = function (answer) {
|
|
30
|
+
if ('othersInput' in answer) {
|
|
31
|
+
return { value: answer.othersInput, isOthersInput: true };
|
|
32
|
+
}
|
|
33
|
+
return { value: answer.value, isOthersInput: false };
|
|
34
|
+
};
|
|
35
|
+
var convertCheckboxAnswer = function (answer) {
|
|
36
|
+
return __assign({ value: answer.value }, (answer.othersInput !== undefined && {
|
|
37
|
+
othersInput: answer.othersInput,
|
|
38
|
+
}));
|
|
39
|
+
};
|
|
40
|
+
var convertAttachmentAnswer = function (answer) {
|
|
41
|
+
return __assign({ value: answer.answer, hasBeenScanned: answer.hasBeenScanned }, (answer.md5Hash !== undefined && { md5Hash: answer.md5Hash }));
|
|
42
|
+
};
|
|
43
|
+
var convertTableAnswer = function (answer) {
|
|
44
|
+
var result = {};
|
|
45
|
+
for (var i = 0; i < answer.length; i++) {
|
|
46
|
+
var rowId = (0, crypto_1.generateUUID)();
|
|
47
|
+
result[rowId] = { rowNum: i, value: answer[i] };
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
var convertChildrenAnswer = function (answer) {
|
|
52
|
+
var _a;
|
|
53
|
+
var result = {};
|
|
54
|
+
for (var i = 0; i < answer.child.length; i++) {
|
|
55
|
+
var childKey = "child".concat(i);
|
|
56
|
+
var value = {};
|
|
57
|
+
for (var j = 0; j < answer.childFields.length; j++) {
|
|
58
|
+
var attr = answer.childFields[j];
|
|
59
|
+
value[attr] = {
|
|
60
|
+
value: (_a = answer.child[i][j]) !== null && _a !== void 0 ? _a : '',
|
|
61
|
+
myInfo: { attr: attr },
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
result[childKey] = { value: value };
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
};
|
|
68
|
+
var convertAddressAnswer = function (answer) {
|
|
69
|
+
var _a;
|
|
70
|
+
var subFields = answer.addressSubFields;
|
|
71
|
+
var result = {};
|
|
72
|
+
for (var _i = 0, ADDRESS_SUBFIELD_KEYS_1 = constants_v4_1.ADDRESS_SUBFIELD_KEYS; _i < ADDRESS_SUBFIELD_KEYS_1.length; _i++) {
|
|
73
|
+
var key = ADDRESS_SUBFIELD_KEYS_1[_i];
|
|
74
|
+
result[key] = { value: (_a = subFields[key]) !== null && _a !== void 0 ? _a : '' };
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
};
|
|
78
|
+
var convertSignatureAnswer = function (answer) {
|
|
79
|
+
return {
|
|
80
|
+
value: answer.value,
|
|
81
|
+
type: 'draw',
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
// since v3 answer types when decrypted in sdk are not well-typed, we do best-effort conversion based on field type
|
|
85
|
+
var convertAnswer = function (fieldType, answer) {
|
|
86
|
+
if (constants_v4_1.GENERIC_STRING_FIELD_TYPES.has(fieldType)) {
|
|
87
|
+
return convertStringAnswer(answer);
|
|
88
|
+
}
|
|
89
|
+
switch (fieldType) {
|
|
90
|
+
case 'yes_no':
|
|
91
|
+
return convertYesNoAnswer(answer);
|
|
92
|
+
case 'email':
|
|
93
|
+
case 'mobile':
|
|
94
|
+
return convertVerifiableAnswer(answer);
|
|
95
|
+
case 'radiobutton':
|
|
96
|
+
return convertRadioAnswer(answer);
|
|
97
|
+
case 'checkbox':
|
|
98
|
+
return convertCheckboxAnswer(answer);
|
|
99
|
+
case 'attachment':
|
|
100
|
+
return convertAttachmentAnswer(answer);
|
|
101
|
+
case 'table':
|
|
102
|
+
return convertTableAnswer(answer);
|
|
103
|
+
case 'children':
|
|
104
|
+
return convertChildrenAnswer(answer);
|
|
105
|
+
case 'address':
|
|
106
|
+
return convertAddressAnswer(answer);
|
|
107
|
+
case 'signature':
|
|
108
|
+
return convertSignatureAnswer(answer);
|
|
109
|
+
default:
|
|
110
|
+
// Safety fallback: coerce to string for any future field types
|
|
111
|
+
return convertStringAnswer(String(answer));
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Converts V3 decrypted responses to V4 normalized shape.
|
|
116
|
+
*
|
|
117
|
+
* @param v3Responses - The V3 responses record (field ID → { fieldType, answer })
|
|
118
|
+
* @param options - Optional provenance metadata to stamp on each response
|
|
119
|
+
* @returns V4 responses record
|
|
120
|
+
*/
|
|
121
|
+
function adaptV3ToV4(v3Responses, options) {
|
|
122
|
+
var _a, _b, _c, _d;
|
|
123
|
+
if (options === void 0) { options = {}; }
|
|
124
|
+
var provenance = (_a = options.provenance) !== null && _a !== void 0 ? _a : {
|
|
125
|
+
submittedAt: new Date().toISOString(),
|
|
126
|
+
};
|
|
127
|
+
var formFields = (_b = options.formFields) !== null && _b !== void 0 ? _b : {};
|
|
128
|
+
var v4Responses = {};
|
|
129
|
+
for (var _i = 0, _e = Object.entries(v3Responses); _i < _e.length; _i++) {
|
|
130
|
+
var _f = _e[_i], fieldId = _f[0], field = _f[1];
|
|
131
|
+
var meta = formFields[fieldId];
|
|
132
|
+
var myInfo = meta === null || meta === void 0 ? void 0 : meta.myInfo;
|
|
133
|
+
var question = myInfo
|
|
134
|
+
? "[Myinfo] ".concat((_c = meta === null || meta === void 0 ? void 0 : meta.question) !== null && _c !== void 0 ? _c : '')
|
|
135
|
+
: ((_d = meta === null || meta === void 0 ? void 0 : meta.question) !== null && _d !== void 0 ? _d : '');
|
|
136
|
+
v4Responses[fieldId] = __assign({ fieldType: field.fieldType, question: question, answer: convertAnswer(field.fieldType, field.answer), provenance: provenance }, (myInfo && { myInfo: myInfo }));
|
|
137
|
+
}
|
|
138
|
+
return v4Responses;
|
|
139
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FormFieldsV3 } from './types';
|
|
2
|
+
import { FieldResponsesV4 } from './types-v4';
|
|
3
|
+
/**
|
|
4
|
+
* Converts V4 normalized responses back to V3 decrypted response shape.
|
|
5
|
+
*
|
|
6
|
+
* @param v4Responses - The V4 responses record (field ID → { fieldType, answer, provenance })
|
|
7
|
+
* @returns V3 responses record (field ID → { fieldType, answer })
|
|
8
|
+
*/
|
|
9
|
+
export declare function adaptV4ToV3(v4Responses: FieldResponsesV4): FormFieldsV3;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.adaptV4ToV3 = adaptV4ToV3;
|
|
15
|
+
var constants_v4_1 = require("./constants-v4");
|
|
16
|
+
function convertStringAnswerToV3(answer) {
|
|
17
|
+
return answer.value;
|
|
18
|
+
}
|
|
19
|
+
function convertYesNoAnswerToV3(answer) {
|
|
20
|
+
return answer.value;
|
|
21
|
+
}
|
|
22
|
+
function convertVerifiableAnswerToV3(answer) {
|
|
23
|
+
return __assign({ value: answer.value }, (answer.signature !== undefined && { signature: answer.signature }));
|
|
24
|
+
}
|
|
25
|
+
function convertRadioAnswerToV3(answer) {
|
|
26
|
+
if (answer.isOthersInput) {
|
|
27
|
+
return { othersInput: answer.value };
|
|
28
|
+
}
|
|
29
|
+
return { value: answer.value };
|
|
30
|
+
}
|
|
31
|
+
function convertCheckboxAnswerToV3(answer) {
|
|
32
|
+
return __assign({ value: answer.value }, (answer.othersInput !== undefined && {
|
|
33
|
+
othersInput: answer.othersInput,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
function convertAttachmentAnswerToV3(answer) {
|
|
37
|
+
return __assign({ answer: answer.value, hasBeenScanned: answer.hasBeenScanned }, (answer.md5Hash !== undefined && { md5Hash: answer.md5Hash }));
|
|
38
|
+
}
|
|
39
|
+
function convertTableAnswerToV3(answer) {
|
|
40
|
+
// Convert { rowId: { rowNum: 0, value: {...} } } to array sorted by rowNum
|
|
41
|
+
var rows = Object.values(answer).sort(function (a, b) { return a.rowNum - b.rowNum; });
|
|
42
|
+
return rows.map(function (row) {
|
|
43
|
+
var result = {};
|
|
44
|
+
for (var _i = 0, _a = Object.entries(row.value); _i < _a.length; _i++) {
|
|
45
|
+
var _b = _a[_i], columnId = _b[0], value = _b[1];
|
|
46
|
+
result[columnId] = String(value);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function convertChildrenAnswerToV3(answer) {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
var childKeys = Object.keys(answer);
|
|
54
|
+
if (childKeys.length === 0) {
|
|
55
|
+
return { child: [], childFields: [] };
|
|
56
|
+
}
|
|
57
|
+
// Extract childFields from the first child
|
|
58
|
+
var firstChild = answer[childKeys[0]];
|
|
59
|
+
var childFields = Object.keys(firstChild.value);
|
|
60
|
+
// Build child array
|
|
61
|
+
var child = [];
|
|
62
|
+
for (var _i = 0, childKeys_1 = childKeys; _i < childKeys_1.length; _i++) {
|
|
63
|
+
var childKey = childKeys_1[_i];
|
|
64
|
+
var childEntry = answer[childKey];
|
|
65
|
+
var childValues = [];
|
|
66
|
+
for (var _c = 0, childFields_1 = childFields; _c < childFields_1.length; _c++) {
|
|
67
|
+
var attr = childFields_1[_c];
|
|
68
|
+
childValues.push((_b = (_a = childEntry.value[attr]) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '');
|
|
69
|
+
}
|
|
70
|
+
child.push(childValues);
|
|
71
|
+
}
|
|
72
|
+
return { child: child, childFields: childFields };
|
|
73
|
+
}
|
|
74
|
+
function convertAddressAnswerToV3(answer) {
|
|
75
|
+
var addressSubFields = {};
|
|
76
|
+
for (var _i = 0, ADDRESS_SUBFIELD_KEYS_1 = constants_v4_1.ADDRESS_SUBFIELD_KEYS; _i < ADDRESS_SUBFIELD_KEYS_1.length; _i++) {
|
|
77
|
+
var key = ADDRESS_SUBFIELD_KEYS_1[_i];
|
|
78
|
+
addressSubFields[key] = answer[key].value;
|
|
79
|
+
}
|
|
80
|
+
return { addressSubFields: addressSubFields };
|
|
81
|
+
}
|
|
82
|
+
function convertSignatureAnswerToV3(answer) {
|
|
83
|
+
return {
|
|
84
|
+
type: 'draw',
|
|
85
|
+
value: answer.value,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Main adaptor */
|
|
89
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
90
|
+
function convertAnswerToV3(fieldType, answer) {
|
|
91
|
+
if (constants_v4_1.GENERIC_STRING_FIELD_TYPES.has(fieldType)) {
|
|
92
|
+
return convertStringAnswerToV3(answer);
|
|
93
|
+
}
|
|
94
|
+
switch (fieldType) {
|
|
95
|
+
case 'yes_no':
|
|
96
|
+
return convertYesNoAnswerToV3(answer);
|
|
97
|
+
case 'email':
|
|
98
|
+
case 'mobile':
|
|
99
|
+
return convertVerifiableAnswerToV3(answer);
|
|
100
|
+
case 'radiobutton':
|
|
101
|
+
return convertRadioAnswerToV3(answer);
|
|
102
|
+
case 'checkbox':
|
|
103
|
+
return convertCheckboxAnswerToV3(answer);
|
|
104
|
+
case 'attachment':
|
|
105
|
+
return convertAttachmentAnswerToV3(answer);
|
|
106
|
+
case 'table':
|
|
107
|
+
return convertTableAnswerToV3(answer);
|
|
108
|
+
case 'children':
|
|
109
|
+
return convertChildrenAnswerToV3(answer);
|
|
110
|
+
case 'address':
|
|
111
|
+
return convertAddressAnswerToV3(answer);
|
|
112
|
+
case 'signature':
|
|
113
|
+
return convertSignatureAnswerToV3(answer);
|
|
114
|
+
default:
|
|
115
|
+
// Safety fallback: coerce to string for any future field types
|
|
116
|
+
return convertStringAnswerToV3(answer);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Converts V4 normalized responses back to V3 decrypted response shape.
|
|
121
|
+
*
|
|
122
|
+
* @param v4Responses - The V4 responses record (field ID → { fieldType, answer, provenance })
|
|
123
|
+
* @returns V3 responses record (field ID → { fieldType, answer })
|
|
124
|
+
*/
|
|
125
|
+
function adaptV4ToV3(v4Responses) {
|
|
126
|
+
var v3Responses = {};
|
|
127
|
+
for (var _i = 0, _a = Object.entries(v4Responses); _i < _a.length; _i++) {
|
|
128
|
+
var _b = _a[_i], fieldId = _b[0], field = _b[1];
|
|
129
|
+
v3Responses[fieldId] = __assign({ fieldType: field.fieldType, answer: convertAnswerToV3(field.fieldType, field.answer) }, (field.myInfo && { myInfo: field.myInfo }));
|
|
130
|
+
}
|
|
131
|
+
return v3Responses;
|
|
132
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isFieldResponsesV4 = exports.adaptV4ToV3 = exports.adaptV3ToV4 = void 0;
|
|
4
|
+
var adapt_v3_to_v4_1 = require("./adapt-v3-to-v4");
|
|
5
|
+
Object.defineProperty(exports, "adaptV3ToV4", { enumerable: true, get: function () { return adapt_v3_to_v4_1.adaptV3ToV4; } });
|
|
6
|
+
var adapt_v4_to_v3_1 = require("./adapt-v4-to-v3");
|
|
7
|
+
Object.defineProperty(exports, "adaptV4ToV3", { enumerable: true, get: function () { return adapt_v4_to_v3_1.adaptV4ToV3; } });
|
|
8
|
+
var crypto_v3_1 = require("./crypto-v3");
|
|
9
|
+
Object.defineProperty(exports, "isFieldResponsesV4", { enumerable: true, get: function () { return crypto_v3_1.isFieldResponsesV4; } });
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ADDRESS_SUBFIELD_KEYS = exports.GENERIC_STRING_FIELD_TYPES = void 0;
|
|
4
|
+
exports.GENERIC_STRING_FIELD_TYPES = new Set([
|
|
5
|
+
'section',
|
|
6
|
+
'statement',
|
|
7
|
+
'image',
|
|
8
|
+
'number',
|
|
9
|
+
'decimal',
|
|
10
|
+
'textfield',
|
|
11
|
+
'textarea',
|
|
12
|
+
'homeno',
|
|
13
|
+
'dropdown',
|
|
14
|
+
'rating',
|
|
15
|
+
'nric',
|
|
16
|
+
'uen',
|
|
17
|
+
'date',
|
|
18
|
+
'country_region',
|
|
19
|
+
]);
|
|
20
|
+
exports.ADDRESS_SUBFIELD_KEYS = [
|
|
21
|
+
'postalCode',
|
|
22
|
+
'blockNumber',
|
|
23
|
+
'streetName',
|
|
24
|
+
'buildingName',
|
|
25
|
+
'levelNumber',
|
|
26
|
+
'unitNumber',
|
|
27
|
+
];
|
package/dist/cjs/crypto-v3.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import CryptoBase from './crypto-base';
|
|
2
2
|
import { DecryptedContentV3, DecryptParams, DecryptParamsV3, EncryptedContentV3 } from './types';
|
|
3
|
+
import { DecryptedContentV4, FieldResponsesV4, FormFieldMeta } from './types-v4';
|
|
4
|
+
/**
|
|
5
|
+
* Checks whether decrypted responses are already in V4 format.
|
|
6
|
+
* V4 entries have a `provenance` object, which V3 entries lack.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isFieldResponsesV4(responses: Record<string, unknown>): responses is FieldResponsesV4;
|
|
3
9
|
export default class CryptoV3 extends CryptoBase {
|
|
4
10
|
signingPublicKey?: string;
|
|
5
11
|
constructor({ signingPublicKey }?: {
|
|
@@ -32,6 +38,17 @@ export default class CryptoV3 extends CryptoBase {
|
|
|
32
38
|
* @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
|
|
33
39
|
*/
|
|
34
40
|
decrypt: (formSecretKey: string, decryptParams: DecryptParamsV3) => DecryptedContentV3 | null;
|
|
41
|
+
/**
|
|
42
|
+
* Decrypts an encrypted submission and returns it in V4 format.
|
|
43
|
+
* If the decrypted data is already in V4 format, it is returned directly.
|
|
44
|
+
* If the decrypted data is in V3 format, it is adapted to V4 using the provided formFields metadata.
|
|
45
|
+
* @param formSecretKey The base-64 encoded form secret key for decrypting the submission.
|
|
46
|
+
* @param decryptParams The params containing encrypted content, encrypted submission key and information.
|
|
47
|
+
* @param formFields A record mapping field IDs to their metadata (question text, myInfo attrs, etc).
|
|
48
|
+
* @returns The decrypted content in V4 format if successful. Else, null will be returned.
|
|
49
|
+
* @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
|
|
50
|
+
*/
|
|
51
|
+
decryptToV4: (formSecretKey: string, decryptParams: DecryptParamsV3, formFields: Record<string, FormFieldMeta>) => DecryptedContentV4 | null;
|
|
35
52
|
/**
|
|
36
53
|
* Returns true if a pair of public & secret keys are associated with each other
|
|
37
54
|
* @param publicKey The public key to verify against.
|
package/dist/cjs/crypto-v3.js
CHANGED
|
@@ -40,11 +40,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
40
40
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
41
|
};
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.isFieldResponsesV4 = isFieldResponsesV4;
|
|
43
44
|
var tweetnacl_util_1 = require("tweetnacl-util");
|
|
44
45
|
var crypto_1 = require("./util/crypto");
|
|
45
46
|
var validate_1 = require("./util/validate");
|
|
47
|
+
var adapt_v3_to_v4_1 = require("./adapt-v3-to-v4");
|
|
46
48
|
var crypto_base_1 = __importDefault(require("./crypto-base"));
|
|
47
49
|
var errors_1 = require("./errors");
|
|
50
|
+
/**
|
|
51
|
+
* Checks whether decrypted responses are already in V4 format.
|
|
52
|
+
* V4 entries have a `provenance` object, which V3 entries lack.
|
|
53
|
+
*/
|
|
54
|
+
function isFieldResponsesV4(responses) {
|
|
55
|
+
var entries = Object.values(responses);
|
|
56
|
+
if (entries.length === 0)
|
|
57
|
+
return false;
|
|
58
|
+
var first = entries[0];
|
|
59
|
+
return typeof first.provenance === 'object' && first.provenance !== null;
|
|
60
|
+
}
|
|
48
61
|
var CryptoV3 = /** @class */ (function (_super) {
|
|
49
62
|
__extends(CryptoV3, _super);
|
|
50
63
|
function CryptoV3(_a) {
|
|
@@ -56,6 +69,7 @@ var CryptoV3 = /** @class */ (function (_super) {
|
|
|
56
69
|
* @param form The base-64 encoded form public key for encrypting.
|
|
57
70
|
* @returns The encrypted basestring.
|
|
58
71
|
*/
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
59
73
|
_this.encrypt = function (msg, formPublicKey) {
|
|
60
74
|
var submissionKeypair = (0, crypto_1.generateKeypair)();
|
|
61
75
|
var encryptedSubmissionSecretKey = (0, crypto_1.encryptMessage)((0, tweetnacl_util_1.decodeBase64)(submissionKeypair.secretKey), formPublicKey);
|
|
@@ -151,6 +165,30 @@ var CryptoV3 = /** @class */ (function (_super) {
|
|
|
151
165
|
return null;
|
|
152
166
|
}
|
|
153
167
|
};
|
|
168
|
+
/**
|
|
169
|
+
* Decrypts an encrypted submission and returns it in V4 format.
|
|
170
|
+
* If the decrypted data is already in V4 format, it is returned directly.
|
|
171
|
+
* If the decrypted data is in V3 format, it is adapted to V4 using the provided formFields metadata.
|
|
172
|
+
* @param formSecretKey The base-64 encoded form secret key for decrypting the submission.
|
|
173
|
+
* @param decryptParams The params containing encrypted content, encrypted submission key and information.
|
|
174
|
+
* @param formFields A record mapping field IDs to their metadata (question text, myInfo attrs, etc).
|
|
175
|
+
* @returns The decrypted content in V4 format if successful. Else, null will be returned.
|
|
176
|
+
* @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
|
|
177
|
+
*/
|
|
178
|
+
_this.decryptToV4 = function (formSecretKey, decryptParams, formFields) {
|
|
179
|
+
var decrypted = _this.decrypt(formSecretKey, decryptParams);
|
|
180
|
+
if (!decrypted)
|
|
181
|
+
return null;
|
|
182
|
+
// If the decrypted responses are already in V4 format, return directly
|
|
183
|
+
var responses = isFieldResponsesV4(decrypted.responses)
|
|
184
|
+
? decrypted.responses
|
|
185
|
+
: (0, adapt_v3_to_v4_1.adaptV3ToV4)(decrypted.responses, { formFields: formFields });
|
|
186
|
+
return {
|
|
187
|
+
submissionSecretKey: decrypted.submissionSecretKey,
|
|
188
|
+
responses: responses,
|
|
189
|
+
verified: decrypted.verified,
|
|
190
|
+
};
|
|
191
|
+
};
|
|
154
192
|
/**
|
|
155
193
|
* Returns true if a pair of public & secret keys are associated with each other
|
|
156
194
|
* @param publicKey The public key to verify against.
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
export type { DecryptedContent, DecryptedContentV3, DecryptParams, DecryptParamsV3, EncryptedAttachmentContent, EncryptedAttachmentRecords, EncryptedContent, EncryptedContentV3, EncryptedFileContent, FieldType, FormField, FormFieldsV3, Keypair, PackageMode, VerificationOptions, } from './types';
|
|
2
1
|
import Crypto from './crypto';
|
|
3
2
|
import CryptoV3 from './crypto-v3';
|
|
4
3
|
import { PackageInitParams } from './types';
|
|
5
4
|
import Verification from './verification';
|
|
6
5
|
import Webhooks from './webhooks';
|
|
6
|
+
export { adaptV3ToV4 } from './adapt-v3-to-v4';
|
|
7
|
+
export { adaptV4ToV3 } from './adapt-v4-to-v3';
|
|
8
|
+
export { isFieldResponsesV4 } from './crypto-v3';
|
|
9
|
+
export { ADDRESS_SUBFIELD_KEYS, GENERIC_STRING_FIELD_TYPES, } from './constants-v4';
|
|
10
|
+
export type { DecryptedContent, DecryptedContentV3, DecryptParams, DecryptParamsV3, EncryptedAttachmentContent, EncryptedAttachmentRecords, EncryptedContent, EncryptedContentV3, EncryptedFileContent, FieldType, FormField, FormFieldsV3, Keypair, PackageMode, VerificationOptions, } from './types';
|
|
11
|
+
export type { AdaptV3ToV4Options, AnswerV4, AddressAnswerV4, AttachmentAnswerV4, CheckboxAnswerV4, ChildrenAnswerV4, DecryptedContentV4, FieldResponsesV4, FieldResponseV4, FormFieldMeta, FormFieldV4, FormFieldsV4, RadioAnswerV4, ResponseProvenance, SignatureAnswerV4, StringAnswerV4, TableAnswerV4, VerifiableAnswerV4, } from './types-v4';
|
|
7
12
|
/**
|
|
8
13
|
* Entrypoint into the FormSG SDK
|
|
9
14
|
*
|
package/dist/cjs/index.js
CHANGED
|
@@ -3,12 +3,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.GENERIC_STRING_FIELD_TYPES = exports.ADDRESS_SUBFIELD_KEYS = exports.isFieldResponsesV4 = exports.adaptV4ToV3 = exports.adaptV3ToV4 = void 0;
|
|
6
7
|
exports.default = default_1;
|
|
7
8
|
var publicKey_1 = require("./util/publicKey");
|
|
8
9
|
var crypto_1 = __importDefault(require("./crypto"));
|
|
9
10
|
var crypto_v3_1 = __importDefault(require("./crypto-v3"));
|
|
10
11
|
var verification_1 = __importDefault(require("./verification"));
|
|
11
12
|
var webhooks_1 = __importDefault(require("./webhooks"));
|
|
13
|
+
var adapt_v3_to_v4_1 = require("./adapt-v3-to-v4");
|
|
14
|
+
Object.defineProperty(exports, "adaptV3ToV4", { enumerable: true, get: function () { return adapt_v3_to_v4_1.adaptV3ToV4; } });
|
|
15
|
+
var adapt_v4_to_v3_1 = require("./adapt-v4-to-v3");
|
|
16
|
+
Object.defineProperty(exports, "adaptV4ToV3", { enumerable: true, get: function () { return adapt_v4_to_v3_1.adaptV4ToV3; } });
|
|
17
|
+
var crypto_v3_2 = require("./crypto-v3");
|
|
18
|
+
Object.defineProperty(exports, "isFieldResponsesV4", { enumerable: true, get: function () { return crypto_v3_2.isFieldResponsesV4; } });
|
|
19
|
+
var constants_v4_1 = require("./constants-v4");
|
|
20
|
+
Object.defineProperty(exports, "ADDRESS_SUBFIELD_KEYS", { enumerable: true, get: function () { return constants_v4_1.ADDRESS_SUBFIELD_KEYS; } });
|
|
21
|
+
Object.defineProperty(exports, "GENERIC_STRING_FIELD_TYPES", { enumerable: true, get: function () { return constants_v4_1.GENERIC_STRING_FIELD_TYPES; } });
|
|
12
22
|
/**
|
|
13
23
|
* Entrypoint into the FormSG SDK
|
|
14
24
|
*
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { FieldType } from './types';
|
|
2
|
+
export type ResponseProvenance = {
|
|
3
|
+
submittedAt?: string;
|
|
4
|
+
stepNumber?: number;
|
|
5
|
+
};
|
|
6
|
+
export type PreviousAnswer = {
|
|
7
|
+
answer: AnswerV4;
|
|
8
|
+
provenance?: ResponseProvenance;
|
|
9
|
+
};
|
|
10
|
+
/** Answer Types */
|
|
11
|
+
export type StringAnswerV4 = {
|
|
12
|
+
value: string;
|
|
13
|
+
};
|
|
14
|
+
export type YesNoAnswerV4 = {
|
|
15
|
+
value: 'Yes' | 'No';
|
|
16
|
+
};
|
|
17
|
+
export type VerifiableAnswerV4 = {
|
|
18
|
+
value: string;
|
|
19
|
+
signature?: string;
|
|
20
|
+
};
|
|
21
|
+
export type RadioAnswerV4 = {
|
|
22
|
+
value: string;
|
|
23
|
+
isOthersInput: boolean;
|
|
24
|
+
};
|
|
25
|
+
export type CheckboxAnswerV4 = {
|
|
26
|
+
value: string[];
|
|
27
|
+
othersInput?: string;
|
|
28
|
+
};
|
|
29
|
+
export type AttachmentAnswerV4 = {
|
|
30
|
+
value: string;
|
|
31
|
+
hasBeenScanned: boolean;
|
|
32
|
+
md5Hash?: string;
|
|
33
|
+
};
|
|
34
|
+
export type TableRowV4 = {
|
|
35
|
+
rowNum: number;
|
|
36
|
+
value: {
|
|
37
|
+
[columnId: string]: string | number;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export type TableAnswerV4 = {
|
|
41
|
+
[rowId: string]: TableRowV4;
|
|
42
|
+
};
|
|
43
|
+
export type ChildSubFieldAnswerV4 = {
|
|
44
|
+
value: string;
|
|
45
|
+
myInfo?: {
|
|
46
|
+
attr: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export type ChildEntryV4 = {
|
|
50
|
+
value: {
|
|
51
|
+
[attr: string]: ChildSubFieldAnswerV4;
|
|
52
|
+
};
|
|
53
|
+
type?: string;
|
|
54
|
+
};
|
|
55
|
+
export type ChildrenAnswerV4 = {
|
|
56
|
+
[childKey: string]: ChildEntryV4;
|
|
57
|
+
};
|
|
58
|
+
export type AddressAnswerV4 = {
|
|
59
|
+
postalCode: StringAnswerV4;
|
|
60
|
+
blockNumber: StringAnswerV4;
|
|
61
|
+
streetName: StringAnswerV4;
|
|
62
|
+
buildingName: StringAnswerV4;
|
|
63
|
+
levelNumber: StringAnswerV4;
|
|
64
|
+
unitNumber: StringAnswerV4;
|
|
65
|
+
};
|
|
66
|
+
export type SignatureAnswerV4 = {
|
|
67
|
+
value: [number, number, number][][];
|
|
68
|
+
type: 'draw';
|
|
69
|
+
};
|
|
70
|
+
export type MyInfoMetaV4 = {
|
|
71
|
+
attr: string;
|
|
72
|
+
};
|
|
73
|
+
export type AnswerV4 = StringAnswerV4 | YesNoAnswerV4 | VerifiableAnswerV4 | RadioAnswerV4 | CheckboxAnswerV4 | AttachmentAnswerV4 | TableAnswerV4 | ChildrenAnswerV4 | AddressAnswerV4 | SignatureAnswerV4;
|
|
74
|
+
export type FieldResponseV4 = {
|
|
75
|
+
fieldType: FieldType;
|
|
76
|
+
answer: AnswerV4;
|
|
77
|
+
question: string;
|
|
78
|
+
provenance: ResponseProvenance;
|
|
79
|
+
previousAnswers?: PreviousAnswer[];
|
|
80
|
+
myInfo?: MyInfoMetaV4;
|
|
81
|
+
};
|
|
82
|
+
export type FieldResponsesV4 = Record<string, FieldResponseV4>;
|
|
83
|
+
type BaseFieldResponseV4 = {
|
|
84
|
+
provenance: ResponseProvenance;
|
|
85
|
+
previousAnswers?: PreviousAnswer[];
|
|
86
|
+
myInfo?: MyInfoMetaV4;
|
|
87
|
+
question: string;
|
|
88
|
+
};
|
|
89
|
+
export type StringFieldResponseV4 = BaseFieldResponseV4 & {
|
|
90
|
+
fieldType: 'section' | 'number' | 'decimal' | 'textfield' | 'textarea' | 'homeno' | 'dropdown' | 'rating' | 'nric' | 'uen' | 'date' | 'country_region';
|
|
91
|
+
answer: StringAnswerV4;
|
|
92
|
+
};
|
|
93
|
+
export type YesNoFieldResponseV4 = BaseFieldResponseV4 & {
|
|
94
|
+
fieldType: 'yes_no';
|
|
95
|
+
answer: YesNoAnswerV4;
|
|
96
|
+
};
|
|
97
|
+
export type VerifiableFieldResponseV4 = BaseFieldResponseV4 & {
|
|
98
|
+
fieldType: 'email' | 'mobile';
|
|
99
|
+
answer: VerifiableAnswerV4;
|
|
100
|
+
};
|
|
101
|
+
export type RadioFieldResponseV4 = BaseFieldResponseV4 & {
|
|
102
|
+
fieldType: 'radiobutton';
|
|
103
|
+
answer: RadioAnswerV4;
|
|
104
|
+
};
|
|
105
|
+
export type CheckboxFieldResponseV4 = BaseFieldResponseV4 & {
|
|
106
|
+
fieldType: 'checkbox';
|
|
107
|
+
answer: CheckboxAnswerV4;
|
|
108
|
+
};
|
|
109
|
+
export type AttachmentFieldResponseV4 = BaseFieldResponseV4 & {
|
|
110
|
+
fieldType: 'attachment';
|
|
111
|
+
answer: AttachmentAnswerV4;
|
|
112
|
+
};
|
|
113
|
+
export type TableFieldResponseV4 = BaseFieldResponseV4 & {
|
|
114
|
+
fieldType: 'table';
|
|
115
|
+
answer: TableAnswerV4;
|
|
116
|
+
};
|
|
117
|
+
export type ChildrenFieldResponseV4 = BaseFieldResponseV4 & {
|
|
118
|
+
fieldType: 'children';
|
|
119
|
+
answer: ChildrenAnswerV4;
|
|
120
|
+
};
|
|
121
|
+
export type AddressFieldResponseV4 = BaseFieldResponseV4 & {
|
|
122
|
+
fieldType: 'address';
|
|
123
|
+
answer: AddressAnswerV4;
|
|
124
|
+
};
|
|
125
|
+
export type SignatureFieldResponseV4 = BaseFieldResponseV4 & {
|
|
126
|
+
fieldType: 'signature';
|
|
127
|
+
answer: SignatureAnswerV4;
|
|
128
|
+
};
|
|
129
|
+
export type FormFieldV4 = StringFieldResponseV4 | YesNoFieldResponseV4 | VerifiableFieldResponseV4 | RadioFieldResponseV4 | CheckboxFieldResponseV4 | AttachmentFieldResponseV4 | TableFieldResponseV4 | ChildrenFieldResponseV4 | AddressFieldResponseV4 | SignatureFieldResponseV4;
|
|
130
|
+
export type FormFieldsV4 = Record<string, FormFieldV4>;
|
|
131
|
+
export type FormFieldMeta = {
|
|
132
|
+
question: string;
|
|
133
|
+
myInfo?: {
|
|
134
|
+
attr: string;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
export type AdaptV3ToV4Options = {
|
|
138
|
+
/** Provenance to stamp on every converted response. */
|
|
139
|
+
provenance?: ResponseProvenance;
|
|
140
|
+
/** Form field definitions keyed by field ID, used to populate question text and myInfo. */
|
|
141
|
+
formFields?: Record<string, FormFieldMeta>;
|
|
142
|
+
};
|
|
143
|
+
export type DecryptedContentV4 = {
|
|
144
|
+
submissionSecretKey: string;
|
|
145
|
+
responses: FieldResponsesV4;
|
|
146
|
+
verified?: Record<string, any>;
|
|
147
|
+
};
|
|
148
|
+
export {};
|
|
@@ -40,3 +40,9 @@ export declare const areAttachmentFieldIdsValid: (fieldIds: string[], filenames:
|
|
|
40
40
|
* @returns EncryptedFileContent The encrypted file content
|
|
41
41
|
*/
|
|
42
42
|
export declare const convertEncryptedAttachmentToFileContent: (encryptedAttachment: EncryptedAttachmentContent) => EncryptedFileContent;
|
|
43
|
+
/**
|
|
44
|
+
* Generates a v4 UUID using the Web Crypto API (crypto.getRandomValues),
|
|
45
|
+
* which is available in both browsers and Node.js (unlike the 'crypto' module).
|
|
46
|
+
* https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid
|
|
47
|
+
*/
|
|
48
|
+
export declare const generateUUID: () => string;
|
package/dist/cjs/util/crypto.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.convertEncryptedAttachmentToFileContent = exports.areAttachmentFieldIdsValid = exports.verifySignedMessage = exports.decryptContent = exports.encryptMessage = exports.generateKeypair = void 0;
|
|
6
|
+
exports.generateUUID = exports.convertEncryptedAttachmentToFileContent = exports.areAttachmentFieldIdsValid = exports.verifySignedMessage = exports.decryptContent = exports.encryptMessage = exports.generateKeypair = void 0;
|
|
7
7
|
var tweetnacl_1 = __importDefault(require("tweetnacl"));
|
|
8
8
|
var tweetnacl_util_1 = require("tweetnacl-util");
|
|
9
9
|
/**
|
|
@@ -84,3 +84,15 @@ var convertEncryptedAttachmentToFileContent = function (encryptedAttachment) { r
|
|
|
84
84
|
binary: (0, tweetnacl_util_1.decodeBase64)(encryptedAttachment.encryptedFile.binary),
|
|
85
85
|
}); };
|
|
86
86
|
exports.convertEncryptedAttachmentToFileContent = convertEncryptedAttachmentToFileContent;
|
|
87
|
+
/**
|
|
88
|
+
* Generates a v4 UUID using the Web Crypto API (crypto.getRandomValues),
|
|
89
|
+
* which is available in both browsers and Node.js (unlike the 'crypto' module).
|
|
90
|
+
* https://stackoverflow.com/questions/105034/how-do-i-create-a-guid-uuid
|
|
91
|
+
*/
|
|
92
|
+
var generateUUID = function () {
|
|
93
|
+
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, function (c) {
|
|
94
|
+
return (+c ^
|
|
95
|
+
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4)))).toString(16);
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
exports.generateUUID = generateUUID;
|