mindoodb 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.
Files changed (170) hide show
  1. package/README.md +263 -0
  2. package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts +5 -0
  3. package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts.map +1 -0
  4. package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js +17 -0
  5. package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js.map +1 -0
  6. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts +111 -0
  7. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -0
  8. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +237 -0
  9. package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -0
  10. package/dist/appendonlystores/network/HttpTransport.d.ts +78 -0
  11. package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -0
  12. package/dist/appendonlystores/network/HttpTransport.js +356 -0
  13. package/dist/appendonlystores/network/HttpTransport.js.map +1 -0
  14. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +131 -0
  15. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -0
  16. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +248 -0
  17. package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -0
  18. package/dist/appendonlystores/network/index.d.ts +8 -0
  19. package/dist/appendonlystores/network/index.d.ts.map +1 -0
  20. package/dist/appendonlystores/network/index.js +21 -0
  21. package/dist/appendonlystores/network/index.js.map +1 -0
  22. package/dist/browser/crypto/BrowserCryptoAdapter.d.ts +14 -0
  23. package/dist/browser/crypto/BrowserCryptoAdapter.d.ts.map +1 -0
  24. package/dist/browser/crypto/BrowserCryptoAdapter.js +30 -0
  25. package/dist/browser/crypto/BrowserCryptoAdapter.js.map +1 -0
  26. package/dist/browser/index.d.ts +3 -0
  27. package/dist/browser/index.d.ts.map +1 -0
  28. package/dist/browser/index.js +26 -0
  29. package/dist/browser/index.js.map +1 -0
  30. package/dist/core/BaseMindooDB.d.ts +182 -0
  31. package/dist/core/BaseMindooDB.d.ts.map +1 -0
  32. package/dist/core/BaseMindooDB.js +1701 -0
  33. package/dist/core/BaseMindooDB.js.map +1 -0
  34. package/dist/core/BaseMindooTenant.d.ts +155 -0
  35. package/dist/core/BaseMindooTenant.d.ts.map +1 -0
  36. package/dist/core/BaseMindooTenant.js +673 -0
  37. package/dist/core/BaseMindooTenant.js.map +1 -0
  38. package/dist/core/BaseMindooTenantDirectory.d.ts +99 -0
  39. package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -0
  40. package/dist/core/BaseMindooTenantDirectory.js +776 -0
  41. package/dist/core/BaseMindooTenantDirectory.js.map +1 -0
  42. package/dist/core/BaseMindooTenantFactory.d.ts +91 -0
  43. package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -0
  44. package/dist/core/BaseMindooTenantFactory.js +297 -0
  45. package/dist/core/BaseMindooTenantFactory.js.map +1 -0
  46. package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts +111 -0
  47. package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts.map +1 -0
  48. package/dist/core/appendonlystores/InMemoryContentAddressedStore.js +293 -0
  49. package/dist/core/appendonlystores/InMemoryContentAddressedStore.js.map +1 -0
  50. package/dist/core/appendonlystores/network/AuthenticationService.d.ts +78 -0
  51. package/dist/core/appendonlystores/network/AuthenticationService.d.ts.map +1 -0
  52. package/dist/core/appendonlystores/network/AuthenticationService.js +297 -0
  53. package/dist/core/appendonlystores/network/AuthenticationService.js.map +1 -0
  54. package/dist/core/appendonlystores/network/NetworkTransport.d.ts +174 -0
  55. package/dist/core/appendonlystores/network/NetworkTransport.d.ts.map +1 -0
  56. package/dist/core/appendonlystores/network/NetworkTransport.js +3 -0
  57. package/dist/core/appendonlystores/network/NetworkTransport.js.map +1 -0
  58. package/dist/core/appendonlystores/network/types.d.ts +135 -0
  59. package/dist/core/appendonlystores/network/types.d.ts.map +1 -0
  60. package/dist/core/appendonlystores/network/types.js +35 -0
  61. package/dist/core/appendonlystores/network/types.js.map +1 -0
  62. package/dist/core/appendonlystores/types.d.ts +157 -0
  63. package/dist/core/appendonlystores/types.d.ts.map +1 -0
  64. package/dist/core/appendonlystores/types.js +3 -0
  65. package/dist/core/appendonlystores/types.js.map +1 -0
  66. package/dist/core/crypto/CryptoAdapter.d.ts +19 -0
  67. package/dist/core/crypto/CryptoAdapter.d.ts.map +1 -0
  68. package/dist/core/crypto/CryptoAdapter.js +3 -0
  69. package/dist/core/crypto/CryptoAdapter.js.map +1 -0
  70. package/dist/core/crypto/MindooDocSigner.d.ts +106 -0
  71. package/dist/core/crypto/MindooDocSigner.d.ts.map +1 -0
  72. package/dist/core/crypto/MindooDocSigner.js +192 -0
  73. package/dist/core/crypto/MindooDocSigner.js.map +1 -0
  74. package/dist/core/crypto/RSAEncryption.d.ts +62 -0
  75. package/dist/core/crypto/RSAEncryption.d.ts.map +1 -0
  76. package/dist/core/crypto/RSAEncryption.js +167 -0
  77. package/dist/core/crypto/RSAEncryption.js.map +1 -0
  78. package/dist/core/errors.d.ts +10 -0
  79. package/dist/core/errors.d.ts.map +1 -0
  80. package/dist/core/errors.js +17 -0
  81. package/dist/core/errors.js.map +1 -0
  82. package/dist/core/index.d.ts +14 -0
  83. package/dist/core/index.d.ts.map +1 -0
  84. package/dist/core/index.js +53 -0
  85. package/dist/core/index.js.map +1 -0
  86. package/dist/core/indexing/virtualviews/IViewEntryAccessCheck.d.ts +33 -0
  87. package/dist/core/indexing/virtualviews/IViewEntryAccessCheck.d.ts.map +1 -0
  88. package/dist/core/indexing/virtualviews/IViewEntryAccessCheck.js +27 -0
  89. package/dist/core/indexing/virtualviews/IViewEntryAccessCheck.js.map +1 -0
  90. package/dist/core/indexing/virtualviews/IVirtualViewDataProvider.d.ts +31 -0
  91. package/dist/core/indexing/virtualviews/IVirtualViewDataProvider.d.ts.map +1 -0
  92. package/dist/core/indexing/virtualviews/IVirtualViewDataProvider.js +3 -0
  93. package/dist/core/indexing/virtualviews/IVirtualViewDataProvider.js.map +1 -0
  94. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.d.ts +52 -0
  95. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.d.ts.map +1 -0
  96. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.js +106 -0
  97. package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.js.map +1 -0
  98. package/dist/core/indexing/virtualviews/ViewEntrySortKey.d.ts +44 -0
  99. package/dist/core/indexing/virtualviews/ViewEntrySortKey.d.ts.map +1 -0
  100. package/dist/core/indexing/virtualviews/ViewEntrySortKey.js +107 -0
  101. package/dist/core/indexing/virtualviews/ViewEntrySortKey.js.map +1 -0
  102. package/dist/core/indexing/virtualviews/ViewEntrySortKeyComparator.d.ts +28 -0
  103. package/dist/core/indexing/virtualviews/ViewEntrySortKeyComparator.d.ts.map +1 -0
  104. package/dist/core/indexing/virtualviews/ViewEntrySortKeyComparator.js +133 -0
  105. package/dist/core/indexing/virtualviews/ViewEntrySortKeyComparator.js.map +1 -0
  106. package/dist/core/indexing/virtualviews/VirtualView.d.ts +109 -0
  107. package/dist/core/indexing/virtualviews/VirtualView.d.ts.map +1 -0
  108. package/dist/core/indexing/virtualviews/VirtualView.js +504 -0
  109. package/dist/core/indexing/virtualviews/VirtualView.js.map +1 -0
  110. package/dist/core/indexing/virtualviews/VirtualViewColumn.d.ts +59 -0
  111. package/dist/core/indexing/virtualviews/VirtualViewColumn.d.ts.map +1 -0
  112. package/dist/core/indexing/virtualviews/VirtualViewColumn.js +70 -0
  113. package/dist/core/indexing/virtualviews/VirtualViewColumn.js.map +1 -0
  114. package/dist/core/indexing/virtualviews/VirtualViewDataChange.d.ts +49 -0
  115. package/dist/core/indexing/virtualviews/VirtualViewDataChange.d.ts.map +1 -0
  116. package/dist/core/indexing/virtualviews/VirtualViewDataChange.js +69 -0
  117. package/dist/core/indexing/virtualviews/VirtualViewDataChange.js.map +1 -0
  118. package/dist/core/indexing/virtualviews/VirtualViewEntryData.d.ts +121 -0
  119. package/dist/core/indexing/virtualviews/VirtualViewEntryData.d.ts.map +1 -0
  120. package/dist/core/indexing/virtualviews/VirtualViewEntryData.js +365 -0
  121. package/dist/core/indexing/virtualviews/VirtualViewEntryData.js.map +1 -0
  122. package/dist/core/indexing/virtualviews/VirtualViewFactory.d.ts +131 -0
  123. package/dist/core/indexing/virtualviews/VirtualViewFactory.d.ts.map +1 -0
  124. package/dist/core/indexing/virtualviews/VirtualViewFactory.js +236 -0
  125. package/dist/core/indexing/virtualviews/VirtualViewFactory.js.map +1 -0
  126. package/dist/core/indexing/virtualviews/VirtualViewNavigator.d.ts +330 -0
  127. package/dist/core/indexing/virtualviews/VirtualViewNavigator.d.ts.map +1 -0
  128. package/dist/core/indexing/virtualviews/VirtualViewNavigator.js +1010 -0
  129. package/dist/core/indexing/virtualviews/VirtualViewNavigator.js.map +1 -0
  130. package/dist/core/indexing/virtualviews/index.d.ts +53 -0
  131. package/dist/core/indexing/virtualviews/index.d.ts.map +1 -0
  132. package/dist/core/indexing/virtualviews/index.js +89 -0
  133. package/dist/core/indexing/virtualviews/index.js.map +1 -0
  134. package/dist/core/indexing/virtualviews/types.d.ts +115 -0
  135. package/dist/core/indexing/virtualviews/types.d.ts.map +1 -0
  136. package/dist/core/indexing/virtualviews/types.js +175 -0
  137. package/dist/core/indexing/virtualviews/types.js.map +1 -0
  138. package/dist/core/keys/KeyBag.d.ts +122 -0
  139. package/dist/core/keys/KeyBag.d.ts.map +1 -0
  140. package/dist/core/keys/KeyBag.js +401 -0
  141. package/dist/core/keys/KeyBag.js.map +1 -0
  142. package/dist/core/types.d.ts +1004 -0
  143. package/dist/core/types.d.ts.map +1 -0
  144. package/dist/core/types.js +10 -0
  145. package/dist/core/types.js.map +1 -0
  146. package/dist/core/userid/index.d.ts +53 -0
  147. package/dist/core/userid/index.d.ts.map +1 -0
  148. package/dist/core/userid/index.js +3 -0
  149. package/dist/core/userid/index.js.map +1 -0
  150. package/dist/core/utils/idGeneration.d.ts +109 -0
  151. package/dist/core/utils/idGeneration.d.ts.map +1 -0
  152. package/dist/core/utils/idGeneration.js +182 -0
  153. package/dist/core/utils/idGeneration.js.map +1 -0
  154. package/dist/core/utils/index.d.ts +5 -0
  155. package/dist/core/utils/index.d.ts.map +1 -0
  156. package/dist/core/utils/index.js +19 -0
  157. package/dist/core/utils/index.js.map +1 -0
  158. package/dist/index.d.ts +7 -0
  159. package/dist/index.d.ts.map +1 -0
  160. package/dist/index.js +40 -0
  161. package/dist/index.js.map +1 -0
  162. package/dist/node/crypto/NodeCryptoAdapter.d.ts +16 -0
  163. package/dist/node/crypto/NodeCryptoAdapter.d.ts.map +1 -0
  164. package/dist/node/crypto/NodeCryptoAdapter.js +53 -0
  165. package/dist/node/crypto/NodeCryptoAdapter.js.map +1 -0
  166. package/dist/node/index.d.ts +3 -0
  167. package/dist/node/index.d.ts.map +1 -0
  168. package/dist/node/index.js +26 -0
  169. package/dist/node/index.js.map +1 -0
  170. package/package.json +58 -0
@@ -0,0 +1,673 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseMindooTenant = void 0;
4
+ const BaseMindooDB_1 = require("./BaseMindooDB");
5
+ const BaseMindooTenantDirectory_1 = require("./BaseMindooTenantDirectory");
6
+ const MindooDocSigner_1 = require("./crypto/MindooDocSigner");
7
+ const errors_1 = require("./errors");
8
+ /**
9
+ * BaseMindooTenant is a platform-agnostic implementation of MindooTenant
10
+ * that works in both browser and server environments.
11
+ *
12
+ * It uses a CryptoAdapter to abstract platform-specific crypto operations,
13
+ * allowing the same implementation to work in browsers and Node.js.
14
+ *
15
+ * Dependencies:
16
+ * - Web Crypto API: Available in both browser (window.crypto) and Node.js (crypto.webcrypto)
17
+ * - AES-256-GCM encryption (widely supported)
18
+ * - Ed25519 signing/verification (Node.js 15+, Chrome 92+)
19
+ * - RSA/ECDH encryption (for encrypting named symmetric keys map)
20
+ * - PBKDF2 key derivation (for password-based key derivation)
21
+ */
22
+ class BaseMindooTenant {
23
+ constructor(factory, tenantId, tenantEncryptionKey, tenantEncryptionKeyPassword, administrationPublicKey, administrationEncryptionPublicKey, currentUser, currentUserPassword, keyBag, storeFactory, cryptoAdapter) {
24
+ this.databaseCache = new Map();
25
+ this.directoryCache = null;
26
+ this.factory = factory;
27
+ this.tenantId = tenantId;
28
+ this.tenantEncryptionKey = tenantEncryptionKey;
29
+ this.tenantEncryptionKeyPassword = tenantEncryptionKeyPassword;
30
+ this.administrationPublicKey = administrationPublicKey;
31
+ this.administrationEncryptionPublicKey = administrationEncryptionPublicKey;
32
+ this.currentUser = currentUser;
33
+ this.currentUserPassword = currentUserPassword;
34
+ this.keyBag = keyBag;
35
+ this.storeFactory = storeFactory;
36
+ // Import createCryptoAdapter dynamically to avoid issues in browser environments
37
+ if (!cryptoAdapter) {
38
+ const { createCryptoAdapter } = require("./crypto/CryptoAdapter");
39
+ this.cryptoAdapter = createCryptoAdapter();
40
+ }
41
+ else {
42
+ this.cryptoAdapter = cryptoAdapter;
43
+ }
44
+ }
45
+ /**
46
+ * Initialize the tenant.
47
+ */
48
+ async initialize() {
49
+ console.log(`[BaseMindooTenant] Initializing tenant ${this.tenantId}`);
50
+ // KeyBag is already loaded by the caller before passing it to the constructor
51
+ const keyCount = (await this.keyBag.listKeys()).length;
52
+ console.log(`[BaseMindooTenant] Tenant initialized with ${keyCount} keys in KeyBag`);
53
+ }
54
+ getCryptoAdapter() {
55
+ return this.cryptoAdapter;
56
+ }
57
+ getFactory() {
58
+ return this.factory;
59
+ }
60
+ getId() {
61
+ return this.tenantId;
62
+ }
63
+ /**
64
+ * Get the administration public key for this tenant.
65
+ * Used for creating document signers for administrative operations.
66
+ *
67
+ * @return The administration public key (Ed25519, PEM format)
68
+ */
69
+ getAdministrationPublicKey() {
70
+ return this.administrationPublicKey;
71
+ }
72
+ /**
73
+ * Get the administration encryption public key for this tenant.
74
+ * Used for encrypting sensitive data in the directory that only admins can decrypt
75
+ * (e.g., usernames in access control documents).
76
+ *
77
+ * @return The administration encryption public key (RSA-OAEP, PEM format)
78
+ */
79
+ getAdministrationEncryptionPublicKey() {
80
+ return this.administrationEncryptionPublicKey;
81
+ }
82
+ getTenantEncryptionKey() {
83
+ return this.tenantEncryptionKey;
84
+ }
85
+ async encryptPayload(payload, decryptionKeyId) {
86
+ console.log(`[BaseMindooTenant] Encrypting payload with key: ${decryptionKeyId}`);
87
+ console.log(`[BaseMindooTenant] Payload size: ${payload.length} bytes`);
88
+ // Get the symmetric key for this key ID
89
+ let symmetricKey;
90
+ try {
91
+ if (decryptionKeyId === "default") {
92
+ console.log(`[BaseMindooTenant] Using default key (tenant encryption key)`);
93
+ // Use cached tenant encryption key if available
94
+ if (this.decryptedTenantKeyCache) {
95
+ symmetricKey = this.decryptedTenantKeyCache;
96
+ console.log(`[BaseMindooTenant] Using cached tenant key, length: ${symmetricKey.length} bytes`);
97
+ }
98
+ else {
99
+ console.log(`[BaseMindooTenant] Decrypting tenant encryption key`);
100
+ // Decrypt tenant encryption key
101
+ const decrypted = await this.decryptPrivateKey(this.tenantEncryptionKey, this.tenantEncryptionKeyPassword, "default");
102
+ symmetricKey = new Uint8Array(decrypted);
103
+ this.decryptedTenantKeyCache = symmetricKey;
104
+ console.log(`[BaseMindooTenant] Decrypted tenant key, length: ${symmetricKey.length} bytes`);
105
+ }
106
+ }
107
+ else {
108
+ console.log(`[BaseMindooTenant] Getting named key from KeyBag: ${decryptionKeyId}`);
109
+ // Get the decrypted key from KeyBag
110
+ const decryptedKey = await this.keyBag.get(decryptionKeyId);
111
+ if (!decryptedKey) {
112
+ throw new errors_1.SymmetricKeyNotFoundError(decryptionKeyId);
113
+ }
114
+ symmetricKey = decryptedKey;
115
+ console.log(`[BaseMindooTenant] Got named key, length: ${symmetricKey.length} bytes`);
116
+ }
117
+ }
118
+ catch (error) {
119
+ console.error(`[BaseMindooTenant] Error getting symmetric key:`, error);
120
+ throw error;
121
+ }
122
+ console.log(`[BaseMindooTenant] Got symmetric key, length: ${symmetricKey.length} bytes`);
123
+ const subtle = this.cryptoAdapter.getSubtle();
124
+ // Bind getRandomValues to maintain 'this' context
125
+ const randomValues = this.cryptoAdapter.getRandomValues.bind(this.cryptoAdapter);
126
+ // Import the symmetric key
127
+ console.log(`[BaseMindooTenant] Importing symmetric key for AES-GCM`);
128
+ let cryptoKey;
129
+ try {
130
+ // Create a new Uint8Array to ensure we have a proper ArrayBuffer
131
+ const keyArray = new Uint8Array(symmetricKey);
132
+ console.log(`[BaseMindooTenant] Key buffer size: ${keyArray.buffer.byteLength} bytes`);
133
+ cryptoKey = await subtle.importKey("raw", keyArray.buffer, { name: "AES-GCM" }, false, // not extractable
134
+ ["encrypt"]);
135
+ console.log(`[BaseMindooTenant] Successfully imported key`);
136
+ }
137
+ catch (error) {
138
+ console.error(`[BaseMindooTenant] Error importing key:`, error);
139
+ console.error(`[BaseMindooTenant] Key length: ${symmetricKey.length}, expected: 32 bytes for AES-256`);
140
+ throw error;
141
+ }
142
+ // Generate IV (12 bytes for AES-GCM)
143
+ console.log(`[BaseMindooTenant] Generating IV`);
144
+ let iv;
145
+ try {
146
+ const ivArray = new Uint8Array(12);
147
+ randomValues(ivArray);
148
+ iv = new Uint8Array(ivArray.buffer, ivArray.byteOffset, ivArray.byteLength);
149
+ console.log(`[BaseMindooTenant] Generated IV: ${iv.length} bytes`);
150
+ }
151
+ catch (error) {
152
+ console.error(`[BaseMindooTenant] Error generating IV:`, error);
153
+ throw error;
154
+ }
155
+ // Encrypt the payload
156
+ console.log(`[BaseMindooTenant] Encrypting payload with AES-GCM`);
157
+ console.log(`[BaseMindooTenant] Payload buffer size: ${payload.buffer.byteLength} bytes`);
158
+ console.log(`[BaseMindooTenant] Payload byteOffset: ${payload.byteOffset}, byteLength: ${payload.byteLength}`);
159
+ let encrypted;
160
+ try {
161
+ // Create a new Uint8Array to ensure we have a proper ArrayBuffer
162
+ const payloadArray = new Uint8Array(payload);
163
+ console.log(`[BaseMindooTenant] Using payload array: ${payloadArray.length} bytes`);
164
+ encrypted = await subtle.encrypt({
165
+ name: "AES-GCM",
166
+ iv: iv,
167
+ tagLength: 128, // 128-bit authentication tag
168
+ }, cryptoKey, payloadArray);
169
+ console.log(`[BaseMindooTenant] Successfully encrypted payload, encrypted size: ${encrypted.byteLength} bytes`);
170
+ }
171
+ catch (error) {
172
+ console.error(`[BaseMindooTenant] Error encrypting payload:`, error);
173
+ console.error(`[BaseMindooTenant] Error details:`, {
174
+ errorName: error instanceof Error ? error.name : typeof error,
175
+ errorMessage: error instanceof Error ? error.message : String(error),
176
+ payloadLength: payload.length,
177
+ keyLength: symmetricKey.length,
178
+ ivLength: iv.length,
179
+ });
180
+ throw error;
181
+ }
182
+ // Combine IV and encrypted data
183
+ // Format: IV (12 bytes) + encrypted data (includes authentication tag)
184
+ console.log(`[BaseMindooTenant] Combining IV and encrypted data`);
185
+ const encryptedArray = new Uint8Array(encrypted);
186
+ const result = new Uint8Array(12 + encryptedArray.length);
187
+ result.set(iv, 0);
188
+ result.set(encryptedArray, 12);
189
+ console.log(`[BaseMindooTenant] Encrypted payload (${payload.length} -> ${result.length} bytes)`);
190
+ return result;
191
+ }
192
+ async decryptPayload(encryptedPayload, decryptionKeyId) {
193
+ console.log(`[BaseMindooTenant] Decrypting payload with key: ${decryptionKeyId}`);
194
+ if (encryptedPayload.length < 12) {
195
+ throw new Error("Encrypted payload too short (missing IV)");
196
+ }
197
+ // Extract IV (first 12 bytes) and encrypted data (rest)
198
+ const iv = encryptedPayload.slice(0, 12);
199
+ const encryptedData = encryptedPayload.slice(12);
200
+ // Get the symmetric key for this key ID
201
+ let symmetricKey;
202
+ if (decryptionKeyId === "default") {
203
+ // Use cached tenant encryption key if available
204
+ if (this.decryptedTenantKeyCache) {
205
+ symmetricKey = this.decryptedTenantKeyCache;
206
+ }
207
+ else {
208
+ // Decrypt tenant encryption key
209
+ const decrypted = await this.decryptPrivateKey(this.tenantEncryptionKey, this.tenantEncryptionKeyPassword, "default");
210
+ symmetricKey = new Uint8Array(decrypted);
211
+ this.decryptedTenantKeyCache = symmetricKey;
212
+ }
213
+ }
214
+ else {
215
+ // Get the decrypted key from KeyBag
216
+ const decryptedKey = await this.keyBag.get(decryptionKeyId);
217
+ if (!decryptedKey) {
218
+ throw new errors_1.SymmetricKeyNotFoundError(decryptionKeyId);
219
+ }
220
+ symmetricKey = decryptedKey;
221
+ }
222
+ const subtle = this.cryptoAdapter.getSubtle();
223
+ // Import the symmetric key
224
+ const cryptoKey = await subtle.importKey("raw", symmetricKey.buffer, { name: "AES-GCM" }, false, // not extractable
225
+ ["decrypt"]);
226
+ // Decrypt the payload
227
+ const decrypted = await subtle.decrypt({
228
+ name: "AES-GCM",
229
+ iv: iv,
230
+ tagLength: 128, // 128-bit authentication tag
231
+ }, cryptoKey, encryptedData.buffer);
232
+ console.log(`[BaseMindooTenant] Decrypted payload (${encryptedPayload.length} -> ${decrypted.byteLength} bytes)`);
233
+ return new Uint8Array(decrypted);
234
+ }
235
+ async signPayload(payload) {
236
+ console.log(`[BaseMindooTenant] Signing payload`);
237
+ // Get the current user's signing private key (decrypted)
238
+ const signingKey = await this.getDecryptedSigningKey();
239
+ const subtle = this.cryptoAdapter.getSubtle();
240
+ // Sign the payload
241
+ const signature = await subtle.sign({
242
+ name: "Ed25519",
243
+ }, signingKey, payload.buffer);
244
+ console.log(`[BaseMindooTenant] Signed payload (signature: ${signature.byteLength} bytes)`);
245
+ return new Uint8Array(signature);
246
+ }
247
+ async signPayloadWithKey(payload, signingKeyPair, password) {
248
+ console.log(`[BaseMindooTenant] Signing payload with provided key`);
249
+ const subtle = this.cryptoAdapter.getSubtle();
250
+ // Decrypt the provided signing private key
251
+ const decryptedKeyBuffer = await this.decryptPrivateKey(signingKeyPair.privateKey, password, "signing");
252
+ // Import the decrypted key as an Ed25519 private key
253
+ const signingKey = await subtle.importKey("pkcs8", decryptedKeyBuffer, {
254
+ name: "Ed25519",
255
+ }, false, // not extractable
256
+ ["sign"]);
257
+ // Sign the payload
258
+ const signature = await subtle.sign({
259
+ name: "Ed25519",
260
+ }, signingKey, payload.buffer);
261
+ console.log(`[BaseMindooTenant] Signed payload with provided key (signature: ${signature.byteLength} bytes)`);
262
+ return new Uint8Array(signature);
263
+ }
264
+ async verifySignature(payload, signature, publicKey) {
265
+ console.log(`[BaseMindooTenant] Verifying signature`);
266
+ const directory = await this.openDirectory();
267
+ const isTrusted = await directory.validatePublicSigningKey(publicKey);
268
+ if (!isTrusted) {
269
+ console.warn(`[BaseMindooTenant] Public key not trusted: ${publicKey}`);
270
+ return false;
271
+ }
272
+ const subtle = this.cryptoAdapter.getSubtle();
273
+ // Convert PEM format to ArrayBuffer
274
+ const publicKeyBuffer = this.pemToArrayBuffer(publicKey);
275
+ // Import the public key from SPKI format
276
+ const cryptoKey = await subtle.importKey("spki", publicKeyBuffer, {
277
+ name: "Ed25519",
278
+ }, false, // not extractable
279
+ ["verify"]);
280
+ // Verify the signature
281
+ const isValid = await subtle.verify({
282
+ name: "Ed25519",
283
+ }, cryptoKey, signature.buffer, payload.buffer);
284
+ console.log(`[BaseMindooTenant] Signature verification result: ${isValid}`);
285
+ return isValid;
286
+ }
287
+ async validatePublicSigningKey(publicKey) {
288
+ console.log(`[BaseMindooTenant] Validating public signing key`);
289
+ // Load the directory database to check if this key belongs to a trusted user
290
+ const directoryDB = await this.openDB("directory");
291
+ // TODO: Query the directory database to check if this public key belongs to a registered,
292
+ // non-revoked user. For now, return true as a placeholder.
293
+ // The directory database should contain user records with their public signing keys.
294
+ // This is a placeholder - in a full implementation, we would:
295
+ // 1. Query the directory database for users with this public key
296
+ // 2. Check if the user is registered and not revoked
297
+ // 3. Return true only if the key belongs to a trusted user
298
+ return true; // TODO: Implement actual validation against directory database
299
+ }
300
+ async getCurrentUserId() {
301
+ // Convert PrivateUserId to PublicUserId
302
+ return {
303
+ username: this.currentUser.username,
304
+ administrationSignature: this.currentUser.administrationSignature,
305
+ userSigningPublicKey: this.currentUser.userSigningKeyPair.publicKey,
306
+ userEncryptionPublicKey: this.currentUser.userEncryptionKeyPair.publicKey,
307
+ };
308
+ }
309
+ async openDirectory() {
310
+ if (!this.directoryCache) {
311
+ this.directoryCache = new BaseMindooTenantDirectory_1.BaseMindooTenantDirectory(this);
312
+ }
313
+ return this.directoryCache;
314
+ }
315
+ async openDB(id, options) {
316
+ // Enforce admin-only mode for directory database - this is a security invariant
317
+ // The directory database must only accept entries signed by the admin key
318
+ const effectiveOptions = id === "directory"
319
+ ? { ...options, adminOnlyDb: true }
320
+ : options ?? {};
321
+ // Return cached database if it exists
322
+ const cached = this.databaseCache.get(id);
323
+ if (cached) {
324
+ // For directory DB, verify admin-only flag matches (defensive check)
325
+ if (id === "directory" && !cached.isAdminOnlyDb()) {
326
+ throw new Error("Directory database was cached without adminOnlyDb - this should never happen");
327
+ }
328
+ return cached;
329
+ }
330
+ // Extract store options and DB-specific options
331
+ const { adminOnlyDb, attachmentConfig, ...storeOptions } = effectiveOptions;
332
+ // Create the database stores using the factory
333
+ const { docStore, attachmentStore } = this.storeFactory.createStore(id, storeOptions);
334
+ const db = new BaseMindooDB_1.BaseMindooDB(this, docStore, attachmentStore, attachmentConfig, adminOnlyDb ?? false);
335
+ await db.initialize();
336
+ // Cache the database for future use
337
+ this.databaseCache.set(id, db);
338
+ return db;
339
+ }
340
+ createDocSignerFor(signKey) {
341
+ console.log(`[BaseMindooTenant] Creating MindooDocSigner for signing key pair`);
342
+ return new MindooDocSigner_1.MindooDocSigner(this, signKey);
343
+ }
344
+ /**
345
+ * Internal method to get the decrypted signing key for the current user.
346
+ */
347
+ async getDecryptedSigningKey() {
348
+ // Use cached signing key if available
349
+ if (this.decryptedUserSigningKeyCache) {
350
+ return this.decryptedUserSigningKeyCache;
351
+ }
352
+ // Decrypt the user's signing private key
353
+ const decryptedKeyBuffer = await this.decryptPrivateKey(this.currentUser.userSigningKeyPair.privateKey, this.currentUserPassword, "signing");
354
+ const subtle = this.cryptoAdapter.getSubtle();
355
+ // Import the decrypted key as an Ed25519 private key
356
+ const cryptoKey = await subtle.importKey("pkcs8", decryptedKeyBuffer, {
357
+ name: "Ed25519",
358
+ }, false, // not extractable
359
+ ["sign"]);
360
+ this.decryptedUserSigningKeyCache = cryptoKey;
361
+ return cryptoKey;
362
+ }
363
+ /**
364
+ * Internal method to decrypt a private key using password-based key derivation.
365
+ * Protected so that BaseMindooTenantDirectory can access it.
366
+ *
367
+ * @param encryptedKey The encrypted private key
368
+ * @param password The password to decrypt the key
369
+ * @param saltString The salt string for key derivation (e.g., "signing", "encryption", "administration", keyId)
370
+ * This is combined with the encryptedKey.salt for additional security
371
+ * @returns The decrypted private key as ArrayBuffer
372
+ */
373
+ async decryptPrivateKey(encryptedKey, password, saltString) {
374
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Starting decryption with saltString: ${saltString}`);
375
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Password length: ${password.length}`);
376
+ console.log(`[BaseMindooTenant] decryptPrivateKey: EncryptedKey iterations: ${encryptedKey.iterations}`);
377
+ const subtle = this.cryptoAdapter.getSubtle();
378
+ // Decode base64 strings
379
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Decoding base64 strings`);
380
+ let ciphertext;
381
+ let iv;
382
+ let tag;
383
+ let saltBytes;
384
+ try {
385
+ ciphertext = this.base64ToUint8Array(encryptedKey.ciphertext);
386
+ iv = this.base64ToUint8Array(encryptedKey.iv);
387
+ tag = this.base64ToUint8Array(encryptedKey.tag);
388
+ saltBytes = this.base64ToUint8Array(encryptedKey.salt);
389
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Decoded - ciphertext: ${ciphertext.length} bytes, iv: ${iv.length} bytes, tag: ${tag.length} bytes, salt: ${saltBytes.length} bytes`);
390
+ }
391
+ catch (error) {
392
+ console.error(`[BaseMindooTenant] decryptPrivateKey: Error decoding base64:`, error);
393
+ throw error;
394
+ }
395
+ // Derive key from password using PBKDF2
396
+ // Combine the stored salt bytes with the salt string for additional security
397
+ // This ensures different key types (signing, encryption, etc.) use different derived keys
398
+ // even if they share the same password
399
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Combining salt with saltString`);
400
+ const saltStringBytes = new TextEncoder().encode(saltString);
401
+ const combinedSalt = new Uint8Array(saltBytes.length + saltStringBytes.length);
402
+ combinedSalt.set(saltBytes);
403
+ combinedSalt.set(saltStringBytes, saltBytes.length);
404
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Combined salt length: ${combinedSalt.length} bytes`);
405
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Importing password key for PBKDF2`);
406
+ let passwordKey;
407
+ try {
408
+ passwordKey = await subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveBits", "deriveKey"]);
409
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Successfully imported password key`);
410
+ }
411
+ catch (error) {
412
+ console.error(`[BaseMindooTenant] decryptPrivateKey: Error importing password key:`, error);
413
+ throw error;
414
+ }
415
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Deriving key with PBKDF2 (iterations: ${encryptedKey.iterations})`);
416
+ let derivedKey;
417
+ try {
418
+ derivedKey = await subtle.deriveKey({
419
+ name: "PBKDF2",
420
+ salt: combinedSalt,
421
+ iterations: encryptedKey.iterations,
422
+ hash: "SHA-256",
423
+ }, passwordKey, {
424
+ name: "AES-GCM",
425
+ length: 256,
426
+ }, false, ["decrypt"]);
427
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Successfully derived key`);
428
+ }
429
+ catch (error) {
430
+ console.error(`[BaseMindooTenant] decryptPrivateKey: Error deriving key:`, error);
431
+ throw error;
432
+ }
433
+ // Combine ciphertext and tag (GCM authentication tag is separate)
434
+ // AES-GCM expects the tag to be appended to the ciphertext
435
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Combining ciphertext and tag`);
436
+ const encryptedData = new Uint8Array(ciphertext.length + tag.length);
437
+ encryptedData.set(ciphertext);
438
+ encryptedData.set(tag, ciphertext.length);
439
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Combined encrypted data length: ${encryptedData.length} bytes`);
440
+ // Decrypt the private key
441
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Decrypting with AES-GCM`);
442
+ console.log(`[BaseMindooTenant] decryptPrivateKey: IV length: ${iv.length} bytes (expected: 12 for AES-GCM)`);
443
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Tag length: ${tag.length} bytes (expected: 16 for 128-bit tag)`);
444
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Ciphertext length: ${ciphertext.length} bytes`);
445
+ let decrypted;
446
+ try {
447
+ // Create a new ArrayBuffer to ensure we have the correct buffer without any offset issues
448
+ const encryptedBuffer = new Uint8Array(encryptedData).buffer;
449
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Encrypted buffer size: ${encryptedBuffer.byteLength} bytes`);
450
+ decrypted = await subtle.decrypt({
451
+ name: "AES-GCM",
452
+ iv: new Uint8Array(iv), // Create a new Uint8Array to ensure proper buffer
453
+ tagLength: 128,
454
+ }, derivedKey, encryptedBuffer);
455
+ console.log(`[BaseMindooTenant] decryptPrivateKey: Successfully decrypted, result length: ${decrypted.byteLength} bytes`);
456
+ }
457
+ catch (error) {
458
+ console.error(`[BaseMindooTenant] decryptPrivateKey: Error during decryption:`, error);
459
+ console.error(`[BaseMindooTenant] decryptPrivateKey: Error details:`, {
460
+ errorName: error instanceof Error ? error.name : typeof error,
461
+ errorMessage: error instanceof Error ? error.message : String(error),
462
+ ivLength: iv.length,
463
+ tagLength: tag.length,
464
+ ciphertextLength: ciphertext.length,
465
+ encryptedDataLength: encryptedData.length,
466
+ iterations: encryptedKey.iterations,
467
+ saltString: saltString,
468
+ encryptedDataByteOffset: encryptedData.byteOffset,
469
+ encryptedDataByteLength: encryptedData.byteLength,
470
+ encryptedDataBufferLength: encryptedData.buffer.byteLength,
471
+ });
472
+ throw error;
473
+ }
474
+ return decrypted;
475
+ }
476
+ /**
477
+ * Helper method to convert base64 string to Uint8Array
478
+ * Protected so that BaseMindooTenantDirectory can access it.
479
+ */
480
+ base64ToUint8Array(base64) {
481
+ const binary = atob(base64);
482
+ const bytes = new Uint8Array(binary.length);
483
+ for (let i = 0; i < binary.length; i++) {
484
+ bytes[i] = binary.charCodeAt(i);
485
+ }
486
+ return bytes;
487
+ }
488
+ /**
489
+ * Helper method to convert Uint8Array to base64 string
490
+ * Protected so that BaseMindooTenantDirectory can access it.
491
+ */
492
+ uint8ArrayToBase64(bytes) {
493
+ let binary = "";
494
+ for (let i = 0; i < bytes.length; i++) {
495
+ binary += String.fromCharCode(bytes[i]);
496
+ }
497
+ return btoa(binary);
498
+ }
499
+ /**
500
+ * Helper method to convert PEM format to ArrayBuffer
501
+ * Removes PEM headers/footers and decodes base64 content
502
+ * Public so that MindooDocSigner and other utilities can use it.
503
+ */
504
+ pemToArrayBuffer(pem) {
505
+ // Remove PEM headers and footers
506
+ const pemHeader = "-----BEGIN PUBLIC KEY-----";
507
+ const pemFooter = "-----END PUBLIC KEY-----";
508
+ let base64 = pem
509
+ .replace(pemHeader, "")
510
+ .replace(pemFooter, "")
511
+ .replace(/\s/g, ""); // Remove all whitespace (newlines, spaces, etc.)
512
+ // Decode base64 to binary string
513
+ const binary = atob(base64);
514
+ // Convert binary string to Uint8Array
515
+ const bytes = new Uint8Array(binary.length);
516
+ for (let i = 0; i < binary.length; i++) {
517
+ bytes[i] = binary.charCodeAt(i);
518
+ }
519
+ return bytes.buffer;
520
+ }
521
+ /**
522
+ * Get the symmetric key for a given key ID.
523
+ * This is a protected helper method that can be used by subclasses.
524
+ *
525
+ * @param decryptionKeyId The key ID ("default" or a named key ID)
526
+ * @returns The decrypted symmetric key bytes
527
+ */
528
+ async getSymmetricKey(decryptionKeyId) {
529
+ if (decryptionKeyId === "default") {
530
+ // Use cached tenant encryption key if available
531
+ if (this.decryptedTenantKeyCache) {
532
+ return this.decryptedTenantKeyCache;
533
+ }
534
+ // Decrypt tenant encryption key
535
+ const decrypted = await this.decryptPrivateKey(this.tenantEncryptionKey, this.tenantEncryptionKeyPassword, "default");
536
+ const symmetricKey = new Uint8Array(decrypted);
537
+ this.decryptedTenantKeyCache = symmetricKey;
538
+ return symmetricKey;
539
+ }
540
+ else {
541
+ // Get the decrypted key from KeyBag
542
+ const decryptedKey = await this.keyBag.get(decryptionKeyId);
543
+ if (!decryptedKey) {
544
+ throw new errors_1.SymmetricKeyNotFoundError(decryptionKeyId);
545
+ }
546
+ return decryptedKey;
547
+ }
548
+ }
549
+ /**
550
+ * Configuration method that determines whether to use deterministic encryption for attachments.
551
+ * Subclasses can override this to change the default behavior.
552
+ *
553
+ * When true (default):
554
+ * - Same plaintext + same key = same ciphertext (enables deduplication)
555
+ * - Reveals when identical content is stored
556
+ *
557
+ * When false:
558
+ * - Each encryption produces unique ciphertext
559
+ * - No deduplication possible
560
+ * - More secure for sensitive content
561
+ *
562
+ * @returns True to use deterministic encryption, false for random IV
563
+ */
564
+ usesDeterministicAttachmentEncryption() {
565
+ return true; // Default: enable deduplication
566
+ }
567
+ /**
568
+ * Encrypt an attachment payload with the appropriate encryption mode.
569
+ * Uses mode prefix format: [mode byte][IV (12 bytes)][encrypted data + tag]
570
+ *
571
+ * For deterministic mode (0x01), the IV is derived from SHA-256(plaintext)[:12]
572
+ * For random mode (0x00), the IV is randomly generated
573
+ *
574
+ * @param payload The plaintext attachment data
575
+ * @param decryptionKeyId The key ID for encryption
576
+ * @returns The encrypted payload with mode prefix
577
+ */
578
+ async encryptAttachmentPayload(payload, decryptionKeyId) {
579
+ console.log(`[BaseMindooTenant] Encrypting attachment payload with key: ${decryptionKeyId}`);
580
+ const symmetricKey = await this.getSymmetricKey(decryptionKeyId);
581
+ const subtle = this.cryptoAdapter.getSubtle();
582
+ const randomValues = this.cryptoAdapter.getRandomValues.bind(this.cryptoAdapter);
583
+ // Import the symmetric key
584
+ const keyArray = new Uint8Array(symmetricKey);
585
+ const cryptoKey = await subtle.importKey("raw", keyArray.buffer, { name: "AES-GCM" }, false, ["encrypt"]);
586
+ // Determine encryption mode and generate IV accordingly
587
+ const useDeterministic = this.usesDeterministicAttachmentEncryption();
588
+ const mode = useDeterministic
589
+ ? BaseMindooTenant.ENCRYPTION_MODE_DETERMINISTIC
590
+ : BaseMindooTenant.ENCRYPTION_MODE_RANDOM;
591
+ let iv;
592
+ if (useDeterministic) {
593
+ // Deterministic IV: derive from plaintext hash
594
+ // IV = SHA-256(plaintext)[:12]
595
+ const payloadBuffer = payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
596
+ const hashBuffer = await subtle.digest("SHA-256", payloadBuffer);
597
+ iv = new Uint8Array(hashBuffer).slice(0, 12);
598
+ console.log(`[BaseMindooTenant] Using deterministic IV derived from content hash`);
599
+ }
600
+ else {
601
+ // Random IV
602
+ iv = new Uint8Array(12);
603
+ randomValues(iv);
604
+ console.log(`[BaseMindooTenant] Using random IV`);
605
+ }
606
+ // Encrypt the payload
607
+ const payloadArray = new Uint8Array(payload);
608
+ const encrypted = await subtle.encrypt({
609
+ name: "AES-GCM",
610
+ iv: iv,
611
+ tagLength: 128,
612
+ }, cryptoKey, payloadArray);
613
+ // Combine: mode byte (1) + IV (12) + encrypted data
614
+ const encryptedArray = new Uint8Array(encrypted);
615
+ const result = new Uint8Array(1 + 12 + encryptedArray.length);
616
+ result[0] = mode;
617
+ result.set(iv, 1);
618
+ result.set(encryptedArray, 13);
619
+ console.log(`[BaseMindooTenant] Encrypted attachment (mode=${mode}, ${payload.length} -> ${result.length} bytes)`);
620
+ return result;
621
+ }
622
+ /**
623
+ * Decrypt an attachment payload that was encrypted with encryptAttachmentPayload.
624
+ * Reads the mode prefix to determine which decryption method to use.
625
+ *
626
+ * @param encryptedPayload The encrypted payload with mode prefix
627
+ * @param decryptionKeyId The key ID for decryption
628
+ * @returns The decrypted plaintext
629
+ */
630
+ async decryptAttachmentPayload(encryptedPayload, decryptionKeyId) {
631
+ console.log(`[BaseMindooTenant] Decrypting attachment payload with key: ${decryptionKeyId}`);
632
+ if (encryptedPayload.length < 13) {
633
+ throw new Error("Invalid encrypted attachment payload: too short");
634
+ }
635
+ // Read mode byte
636
+ const mode = encryptedPayload[0];
637
+ if (mode !== BaseMindooTenant.ENCRYPTION_MODE_RANDOM &&
638
+ mode !== BaseMindooTenant.ENCRYPTION_MODE_DETERMINISTIC) {
639
+ throw new Error(`Invalid encryption mode: ${mode}`);
640
+ }
641
+ console.log(`[BaseMindooTenant] Decrypting attachment with mode=${mode}`);
642
+ // Extract IV (bytes 1-12)
643
+ const iv = encryptedPayload.slice(1, 13);
644
+ // Extract ciphertext (bytes 13 onwards)
645
+ const ciphertext = encryptedPayload.slice(13);
646
+ const symmetricKey = await this.getSymmetricKey(decryptionKeyId);
647
+ const subtle = this.cryptoAdapter.getSubtle();
648
+ // Import the symmetric key
649
+ const keyArray = new Uint8Array(symmetricKey);
650
+ const cryptoKey = await subtle.importKey("raw", keyArray.buffer, { name: "AES-GCM" }, false, ["decrypt"]);
651
+ // Decrypt
652
+ const decrypted = await subtle.decrypt({
653
+ name: "AES-GCM",
654
+ iv: iv,
655
+ tagLength: 128,
656
+ }, cryptoKey, ciphertext);
657
+ const result = new Uint8Array(decrypted);
658
+ console.log(`[BaseMindooTenant] Decrypted attachment (${encryptedPayload.length} -> ${result.length} bytes)`);
659
+ return result;
660
+ }
661
+ }
662
+ exports.BaseMindooTenant = BaseMindooTenant;
663
+ // ============================================================================
664
+ // Attachment Encryption Methods
665
+ // ============================================================================
666
+ /**
667
+ * Encryption mode byte values for attachment payloads.
668
+ * 0x00 = Random IV (more secure, no deduplication)
669
+ * 0x01 = Deterministic IV (enables deduplication, reveals identical content)
670
+ */
671
+ BaseMindooTenant.ENCRYPTION_MODE_RANDOM = 0x00;
672
+ BaseMindooTenant.ENCRYPTION_MODE_DETERMINISTIC = 0x01;
673
+ //# sourceMappingURL=BaseMindooTenant.js.map