@dereekb/firebase-server 13.0.7 → 13.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/index.cjs.js +149 -26
- package/index.cjs.js.map +1 -1
- package/index.esm.js +150 -29
- package/index.esm.js.map +1 -1
- package/mailgun/package.json +8 -8
- package/model/index.cjs.js +34 -40
- package/model/index.esm.js +35 -41
- package/model/package.json +8 -8
- package/model/src/lib/notification/notification.action.init.service.d.ts +1 -1
- package/model/src/lib/notification/notification.action.service.d.ts +3 -3
- package/model/src/lib/notification/notification.expedite.service.d.ts +4 -4
- package/model/src/lib/storagefile/storagefile.action.init.service.d.ts +1 -1
- package/model/src/lib/storagefile/storagefile.action.server.d.ts +1 -1
- package/package.json +10 -10
- package/src/lib/firestore/index.d.ts +1 -0
- package/src/lib/firestore/snapshot/index.d.ts +1 -0
- package/src/lib/firestore/snapshot/snapshot.field.d.ts +80 -0
- package/src/lib/nest/function/context.d.ts +9 -12
- package/test/package.json +8 -8
- package/zoho/package.json +8 -8
package/index.cjs.js
CHANGED
|
@@ -7,6 +7,7 @@ var date = require('@dereekb/date');
|
|
|
7
7
|
var makeError = require('make-error');
|
|
8
8
|
var rxjs = require('rxjs');
|
|
9
9
|
var firestore = require('@google-cloud/firestore');
|
|
10
|
+
var crypto = require('crypto');
|
|
10
11
|
var common = require('@nestjs/common');
|
|
11
12
|
var nestjs = require('@dereekb/nestjs');
|
|
12
13
|
var v2 = require('firebase-functions/v2');
|
|
@@ -1048,6 +1049,140 @@ function googleCloudFirestoreDrivers() {
|
|
|
1048
1049
|
*/
|
|
1049
1050
|
const googleCloudFirestoreContextFactory = firebase.firestoreContextFactory(googleCloudFirestoreDrivers());
|
|
1050
1051
|
|
|
1052
|
+
// MARK: Encrypted Field
|
|
1053
|
+
/**
|
|
1054
|
+
* AES-256-GCM encryption constants.
|
|
1055
|
+
*/
|
|
1056
|
+
const ENCRYPTED_FIELD_ALGORITHM = 'aes-256-gcm';
|
|
1057
|
+
const ENCRYPTED_FIELD_IV_LENGTH = 12;
|
|
1058
|
+
const ENCRYPTED_FIELD_AUTH_TAG_LENGTH = 16;
|
|
1059
|
+
const ENCRYPTED_FIELD_KEY_LENGTH = 32;
|
|
1060
|
+
/**
|
|
1061
|
+
* Resolves the encryption key Buffer from a secret source.
|
|
1062
|
+
*
|
|
1063
|
+
* @param source - The secret source configuration.
|
|
1064
|
+
* @returns A 32-byte Buffer for AES-256 encryption.
|
|
1065
|
+
* @throws Error if the resolved key is not 64 hex characters.
|
|
1066
|
+
*/
|
|
1067
|
+
function resolveEncryptionKey(source) {
|
|
1068
|
+
let hex;
|
|
1069
|
+
if (typeof source === 'string') {
|
|
1070
|
+
hex = source;
|
|
1071
|
+
}
|
|
1072
|
+
else if (typeof source === 'function') {
|
|
1073
|
+
hex = source();
|
|
1074
|
+
}
|
|
1075
|
+
else {
|
|
1076
|
+
const envValue = process.env[source.env];
|
|
1077
|
+
if (!envValue) {
|
|
1078
|
+
throw new Error(`firestoreEncryptedField: environment variable "${source.env}" is not set.`);
|
|
1079
|
+
}
|
|
1080
|
+
hex = envValue;
|
|
1081
|
+
}
|
|
1082
|
+
if (hex.length !== ENCRYPTED_FIELD_KEY_LENGTH * 2) {
|
|
1083
|
+
throw new Error(`firestoreEncryptedField: expected a ${ENCRYPTED_FIELD_KEY_LENGTH * 2}-character hex key, got ${hex.length} characters.`);
|
|
1084
|
+
}
|
|
1085
|
+
return Buffer.from(hex, 'hex');
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Encrypts a JSON-serializable value to a base64-encoded string.
|
|
1089
|
+
*
|
|
1090
|
+
* Format: base64(IV (12 bytes) + ciphertext + authTag (16 bytes))
|
|
1091
|
+
*
|
|
1092
|
+
* @param value - The value to encrypt.
|
|
1093
|
+
* @param key - The 32-byte encryption key.
|
|
1094
|
+
* @returns The encrypted value as a base64 string.
|
|
1095
|
+
*/
|
|
1096
|
+
function encryptValue(value, key) {
|
|
1097
|
+
const iv = crypto.randomBytes(ENCRYPTED_FIELD_IV_LENGTH);
|
|
1098
|
+
const cipher = crypto.createCipheriv(ENCRYPTED_FIELD_ALGORITHM, key, iv);
|
|
1099
|
+
const plaintext = JSON.stringify(value);
|
|
1100
|
+
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
|
|
1101
|
+
const authTag = cipher.getAuthTag();
|
|
1102
|
+
const combined = Buffer.concat([iv, encrypted, authTag]);
|
|
1103
|
+
return combined.toString('base64');
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Decrypts a base64-encoded string back to the original value.
|
|
1107
|
+
*
|
|
1108
|
+
* @param encoded - The base64-encoded encrypted string (IV + ciphertext + authTag).
|
|
1109
|
+
* @param key - The 32-byte encryption key.
|
|
1110
|
+
* @returns The decrypted value.
|
|
1111
|
+
*/
|
|
1112
|
+
function decryptValue(encoded, key) {
|
|
1113
|
+
const combined = Buffer.from(encoded, 'base64');
|
|
1114
|
+
const iv = combined.subarray(0, ENCRYPTED_FIELD_IV_LENGTH);
|
|
1115
|
+
const authTag = combined.subarray(combined.length - ENCRYPTED_FIELD_AUTH_TAG_LENGTH);
|
|
1116
|
+
const ciphertext = combined.subarray(ENCRYPTED_FIELD_IV_LENGTH, combined.length - ENCRYPTED_FIELD_AUTH_TAG_LENGTH);
|
|
1117
|
+
const decipher = crypto.createDecipheriv(ENCRYPTED_FIELD_ALGORITHM, key, iv);
|
|
1118
|
+
decipher.setAuthTag(authTag);
|
|
1119
|
+
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
1120
|
+
return JSON.parse(decrypted.toString('utf8'));
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Creates a Firestore field mapping that encrypts/decrypts a JSON-serializable value
|
|
1124
|
+
* using AES-256-GCM. The value is stored in Firestore as a base64-encoded string.
|
|
1125
|
+
*
|
|
1126
|
+
* The encryption key is resolved from the configured secret source on each read/write,
|
|
1127
|
+
* allowing for key rotation via environment variable changes.
|
|
1128
|
+
*
|
|
1129
|
+
* @example
|
|
1130
|
+
* ```typescript
|
|
1131
|
+
* const jwksField = firestoreEncryptedField<JWKSet>({
|
|
1132
|
+
* secret: { env: 'FIRESTORE_ENCRYPTION_KEY' },
|
|
1133
|
+
* default: () => ({ keys: [] })
|
|
1134
|
+
* });
|
|
1135
|
+
* ```
|
|
1136
|
+
*
|
|
1137
|
+
* @template T - The JSON-serializable value type.
|
|
1138
|
+
* @param config - Encryption field configuration.
|
|
1139
|
+
* @returns A field mapping configuration for encrypted values.
|
|
1140
|
+
*/
|
|
1141
|
+
function firestoreEncryptedField(config) {
|
|
1142
|
+
const { secret, default: defaultValue } = config;
|
|
1143
|
+
return firebase.firestoreField({
|
|
1144
|
+
default: defaultValue,
|
|
1145
|
+
fromData: (data) => {
|
|
1146
|
+
const key = resolveEncryptionKey(secret);
|
|
1147
|
+
return decryptValue(data, key);
|
|
1148
|
+
},
|
|
1149
|
+
toData: (value) => {
|
|
1150
|
+
const key = resolveEncryptionKey(secret);
|
|
1151
|
+
return encryptValue(value, key);
|
|
1152
|
+
}
|
|
1153
|
+
});
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Creates a Firestore field mapping for an optional encrypted field.
|
|
1157
|
+
*
|
|
1158
|
+
* When the value is null/undefined, it is stored/read as null. When present, it is
|
|
1159
|
+
* encrypted/decrypted using AES-256-GCM.
|
|
1160
|
+
*
|
|
1161
|
+
* @example
|
|
1162
|
+
* ```typescript
|
|
1163
|
+
* const optionalSecretField = optionalFirestoreEncryptedField<OAuthClientSecret>({
|
|
1164
|
+
* secret: { env: 'FIRESTORE_ENCRYPTION_KEY' }
|
|
1165
|
+
* });
|
|
1166
|
+
* ```
|
|
1167
|
+
*
|
|
1168
|
+
* @template T - The JSON-serializable value type.
|
|
1169
|
+
* @param config - Encryption field configuration.
|
|
1170
|
+
* @returns A field mapping configuration for optional encrypted values.
|
|
1171
|
+
*/
|
|
1172
|
+
function optionalFirestoreEncryptedField(config) {
|
|
1173
|
+
const { secret } = config;
|
|
1174
|
+
return firebase.optionalFirestoreField({
|
|
1175
|
+
transformFromData: (data) => {
|
|
1176
|
+
const key = resolveEncryptionKey(secret);
|
|
1177
|
+
return decryptValue(data, key);
|
|
1178
|
+
},
|
|
1179
|
+
transformToData: (value) => {
|
|
1180
|
+
const key = resolveEncryptionKey(secret);
|
|
1181
|
+
return encryptValue(value, key);
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1051
1186
|
function assertContextHasAuth(context) {
|
|
1052
1187
|
if (!isContextWithAuthData(context)) {
|
|
1053
1188
|
throw unauthenticatedContextHasNoUidError();
|
|
@@ -1153,10 +1288,6 @@ function __param(paramIndex, decorator) {
|
|
|
1153
1288
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
1154
1289
|
}
|
|
1155
1290
|
|
|
1156
|
-
function __metadata(metadataKey, metadataValue) {
|
|
1157
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
1291
|
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
1161
1292
|
var e = new Error(message);
|
|
1162
1293
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
@@ -1734,43 +1865,34 @@ function firebaseServerActionsTransformContext(options) {
|
|
|
1734
1865
|
}
|
|
1735
1866
|
const FIREBASE_SERVER_VALIDATION_ERROR_CODE = 'VALIDATION_ERROR';
|
|
1736
1867
|
/**
|
|
1737
|
-
*
|
|
1738
|
-
* @param validationError
|
|
1739
|
-
* @returns
|
|
1868
|
+
* Creates a server error object from ArkType validation errors.
|
|
1740
1869
|
*/
|
|
1741
|
-
function firebaseServerValidationServerError(
|
|
1742
|
-
const nestValidationExceptionFactory = new common.ValidationPipe({
|
|
1743
|
-
forbidUnknownValues: false
|
|
1744
|
-
}).createExceptionFactory();
|
|
1745
|
-
const nestError = nestValidationExceptionFactory(validationError);
|
|
1746
|
-
const data = nestError.getResponse();
|
|
1870
|
+
function firebaseServerValidationServerError(validationErrors) {
|
|
1747
1871
|
return {
|
|
1748
1872
|
message: 'One or more data/form validation errors occurred.',
|
|
1749
1873
|
code: FIREBASE_SERVER_VALIDATION_ERROR_CODE,
|
|
1750
|
-
data
|
|
1874
|
+
data: {
|
|
1875
|
+
message: validationErrors.summary
|
|
1876
|
+
}
|
|
1751
1877
|
};
|
|
1752
1878
|
}
|
|
1753
1879
|
/**
|
|
1754
1880
|
* Creates a new badRequestError with the validation error details as the response data.
|
|
1755
|
-
*
|
|
1756
|
-
* @param validationError
|
|
1757
|
-
* @returns
|
|
1758
1881
|
*/
|
|
1759
|
-
function firebaseServerValidationError(
|
|
1760
|
-
const serverError = firebaseServerValidationServerError(
|
|
1882
|
+
function firebaseServerValidationError(validationErrors) {
|
|
1883
|
+
const serverError = firebaseServerValidationServerError(validationErrors);
|
|
1761
1884
|
return badRequestError(serverError);
|
|
1762
1885
|
}
|
|
1763
1886
|
function firebaseServerActionsTransformFactory(options) {
|
|
1764
|
-
const { logError
|
|
1887
|
+
const { logError } = options ?? {};
|
|
1765
1888
|
const logErrorFunction = logError !== false ? (typeof logError === 'function' ? logError : defaultFirebaseServerActionsTransformFactoryLogErrorFunction) : util.mapIdentityFunction;
|
|
1766
1889
|
return model.transformAndValidateObjectFactory({
|
|
1767
|
-
handleValidationError: (
|
|
1768
|
-
const serverError = firebaseServerValidationServerError(
|
|
1890
|
+
handleValidationError: async (validationErrors) => {
|
|
1891
|
+
const serverError = firebaseServerValidationServerError(validationErrors);
|
|
1769
1892
|
const { data } = serverError;
|
|
1770
1893
|
logErrorFunction(data);
|
|
1771
1894
|
throw badRequestError(serverError);
|
|
1772
|
-
}
|
|
1773
|
-
defaultValidationOptions
|
|
1895
|
+
}
|
|
1774
1896
|
});
|
|
1775
1897
|
}
|
|
1776
1898
|
|
|
@@ -1829,8 +1951,7 @@ exports.FirebaseAppCheckMiddleware = class FirebaseAppCheckMiddleware {
|
|
|
1829
1951
|
exports.FirebaseAppCheckMiddleware = __decorate([
|
|
1830
1952
|
common.Injectable(),
|
|
1831
1953
|
__param(0, common.Optional()),
|
|
1832
|
-
__param(0, common.Inject(GlobalRoutePrefixConfig))
|
|
1833
|
-
__metadata("design:paramtypes", [Object])
|
|
1954
|
+
__param(0, common.Inject(GlobalRoutePrefixConfig))
|
|
1834
1955
|
], exports.FirebaseAppCheckMiddleware);
|
|
1835
1956
|
/**
|
|
1836
1957
|
* Verifies the AppCheck parameter. If it fails, a value is returned.
|
|
@@ -2780,6 +2901,7 @@ exports.firebaseServerStorageModuleMetadata = firebaseServerStorageModuleMetadat
|
|
|
2780
2901
|
exports.firebaseServerValidationError = firebaseServerValidationError;
|
|
2781
2902
|
exports.firebaseServerValidationServerError = firebaseServerValidationServerError;
|
|
2782
2903
|
exports.firestoreClientQueryConstraintFunctionsDriver = firestoreClientQueryConstraintFunctionsDriver;
|
|
2904
|
+
exports.firestoreEncryptedField = firestoreEncryptedField;
|
|
2783
2905
|
exports.firestoreServerIncrementUpdateToUpdateData = firestoreServerIncrementUpdateToUpdateData;
|
|
2784
2906
|
exports.forbiddenError = forbiddenError;
|
|
2785
2907
|
exports.getAuthUserOrUndefined = getAuthUserOrUndefined;
|
|
@@ -2836,6 +2958,7 @@ exports.onCallUpdateModel = onCallUpdateModel;
|
|
|
2836
2958
|
exports.onScheduleHandlerWithNestApplicationFactory = onScheduleHandlerWithNestApplicationFactory;
|
|
2837
2959
|
exports.onScheduleHandlerWithNestContextFactory = onScheduleHandlerWithNestContextFactory;
|
|
2838
2960
|
exports.optionalAuthContext = optionalAuthContext;
|
|
2961
|
+
exports.optionalFirestoreEncryptedField = optionalFirestoreEncryptedField;
|
|
2839
2962
|
exports.permissionDeniedError = permissionDeniedError;
|
|
2840
2963
|
exports.phoneNumberAlreadyExistsError = phoneNumberAlreadyExistsError;
|
|
2841
2964
|
exports.preconditionConflictError = preconditionConflictError;
|