TestConfig.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import fs from 'node:fs'
  2. import Path from 'node:path'
  3. import crypto from 'node:crypto'
  4. import { RootKeyEncryptionKey } from '@overleaf/object-persistor/src/PerProjectEncryptedS3Persistor.js'
  5. const AWS_S3_USER_FILES_STORAGE_CLASS =
  6. process.env.AWS_S3_USER_FILES_STORAGE_CLASS
  7. // use functions to get a fresh copy, not a reference, each time
  8. function s3BaseConfig() {
  9. return {
  10. endpoint: process.env.AWS_S3_ENDPOINT,
  11. pathStyle: true,
  12. partSize: 100 * 1024 * 1024,
  13. ca: [fs.readFileSync('/certs/public.crt')],
  14. }
  15. }
  16. function s3Config() {
  17. return {
  18. key: process.env.AWS_ACCESS_KEY_ID,
  19. secret: process.env.AWS_SECRET_ACCESS_KEY,
  20. ...s3BaseConfig(),
  21. }
  22. }
  23. const S3SSECKeys = [
  24. new RootKeyEncryptionKey(
  25. crypto.generateKeySync('aes', { length: 256 }).export(),
  26. Buffer.alloc(32)
  27. ),
  28. ]
  29. function s3SSECConfig() {
  30. return {
  31. ...s3Config(),
  32. ignoreErrorsFromDEKReEncryption: false,
  33. automaticallyRotateDEKEncryption: true,
  34. dataEncryptionKeyBucketName: process.env.AWS_S3_USER_FILES_DEK_BUCKET_NAME,
  35. pathToProjectFolder(_bucketName, path) {
  36. const match = path.match(/^[a-f0-9]{24}\//)
  37. if (!match) throw new Error('not a project-folder')
  38. const [projectFolder] = match
  39. return projectFolder
  40. },
  41. async getRootKeyEncryptionKeys() {
  42. return S3SSECKeys
  43. },
  44. storageClass: {
  45. [process.env.AWS_S3_TEMPLATE_FILES_BUCKET_NAME]:
  46. AWS_S3_USER_FILES_STORAGE_CLASS,
  47. },
  48. }
  49. }
  50. function s3ConfigDefaultProviderCredentials() {
  51. return {
  52. ...s3BaseConfig(),
  53. }
  54. }
  55. function s3Stores() {
  56. return {
  57. template_files: process.env.AWS_S3_TEMPLATE_FILES_BUCKET_NAME,
  58. }
  59. }
  60. function gcsConfig() {
  61. return {
  62. endpoint: {
  63. apiEndpoint: process.env.GCS_API_ENDPOINT,
  64. projectId: 'fake',
  65. },
  66. directoryKeyRegex: /^[0-9a-fA-F]{24}\/[0-9a-fA-F]{24}/,
  67. unlockBeforeDelete: false, // fake-gcs does not support this
  68. deletedBucketSuffix: '-deleted',
  69. }
  70. }
  71. function gcsStores() {
  72. return {
  73. template_files: process.env.GCS_TEMPLATE_FILES_BUCKET_NAME,
  74. }
  75. }
  76. function fsStores() {
  77. return {
  78. template_files: Path.resolve(
  79. import.meta.dirname,
  80. '../../../template_files'
  81. ),
  82. }
  83. }
  84. function fallbackStores(primaryConfig, fallbackConfig) {
  85. return {
  86. [primaryConfig.template_files]: fallbackConfig.template_files,
  87. }
  88. }
  89. const BackendSettings = {
  90. SHARD_01_FSPersistor: {
  91. backend: 'fs',
  92. stores: fsStores(),
  93. },
  94. SHARD_01_S3Persistor: {
  95. backend: 's3',
  96. s3: s3Config(),
  97. stores: s3Stores(),
  98. },
  99. SHARD_01_S3PersistorDefaultProviderCredentials: {
  100. backend: 's3',
  101. s3: s3ConfigDefaultProviderCredentials(),
  102. stores: s3Stores(),
  103. },
  104. SHARD_01_GcsPersistor: {
  105. backend: 'gcs',
  106. gcs: gcsConfig(),
  107. stores: gcsStores(),
  108. },
  109. SHARD_01_PerProjectEncryptedS3Persistor: {
  110. backend: 's3SSEC',
  111. s3SSEC: s3SSECConfig(),
  112. stores: s3Stores(),
  113. },
  114. SHARD_02_FallbackS3ToFSPersistor: {
  115. backend: 's3',
  116. s3: s3Config(),
  117. stores: s3Stores(),
  118. fallback: {
  119. backend: 'fs',
  120. buckets: fallbackStores(s3Stores(), fsStores()),
  121. },
  122. },
  123. SHARD_02_FallbackFSToS3Persistor: {
  124. backend: 'fs',
  125. s3: s3Config(),
  126. stores: fsStores(),
  127. fallback: {
  128. backend: 's3',
  129. buckets: fallbackStores(fsStores(), s3Stores()),
  130. },
  131. },
  132. SHARD_03_FallbackGcsToS3Persistor: {
  133. backend: 'gcs',
  134. gcs: gcsConfig(),
  135. stores: gcsStores(),
  136. s3: s3Config(),
  137. fallback: {
  138. backend: 's3',
  139. buckets: fallbackStores(gcsStores(), s3Stores()),
  140. },
  141. },
  142. SHARD_03_FallbackS3ToGcsPersistor: {
  143. backend: 's3',
  144. // can use the same bucket names for gcs and s3 (in tests)
  145. stores: s3Stores(),
  146. s3: s3Config(),
  147. gcs: gcsConfig(),
  148. fallback: {
  149. backend: 'gcs',
  150. buckets: fallbackStores(s3Stores(), gcsStores()),
  151. },
  152. },
  153. }
  154. function checkForUnexpectedTestFile() {
  155. const awareOfSharding = [
  156. 'FilestoreApp.js',
  157. 'FilestoreTests.js',
  158. 'TestConfig.js',
  159. 'TestHelper.js',
  160. ]
  161. for (const file of fs.readdirSync(import.meta.dirname).sort()) {
  162. if (!awareOfSharding.includes(file)) {
  163. throw new Error(
  164. `Found new test file ${file}: All tests must be aware of the SHARD_ prefix.`
  165. )
  166. }
  167. }
  168. }
  169. checkForUnexpectedTestFile()
  170. export default {
  171. AWS_S3_USER_FILES_STORAGE_CLASS,
  172. BackendSettings,
  173. s3Config,
  174. s3SSECConfig,
  175. }