@@ -2,13 +2,17 @@ import { MongoClient, MongoClientOptions } from "mongodb";
2
2
import type { SrvRecord } from "dns" ;
3
3
import { promises as netPromises } from "dns" ;
4
4
import { nanoid } from "nanoid" ;
5
- import path from "path" ;
5
+ import path , { dirname , resolve } from "path" ;
6
6
import mongoUri from "mongodb-uri" ;
7
7
import * as crypto from "crypto" ;
8
+ import axios from 'axios' ;
8
9
import tunnel , { Config } from "tunnel-ssh" ;
10
+ import { Buffer } from 'buffer' ;
9
11
10
12
import { ARK_FOLDER_PATH } from "../../../utils/constants" ;
11
13
import { Connection } from "." ;
14
+ import { mkdir , readFile , writeFile } from "fs/promises" ;
15
+ import { existsSync } from "fs" ;
12
16
13
17
export const sshTunnel = async (
14
18
sshConfig : Ark . StoredConnection [ "ssh" ] ,
@@ -51,17 +55,22 @@ export const sshTunnel = async (
51
55
} ) ;
52
56
} ;
53
57
54
- export const getConnectionUri = (
58
+ export const getConnectionUri = async (
55
59
{
56
60
hosts,
57
61
database = "admin" ,
58
62
username,
59
63
password,
60
64
options,
61
65
iv,
62
- key
66
+ key,
67
+ encryptionKeySource
63
68
} : Ark . StoredConnection
64
69
) => {
70
+ const pwd = ( password && key && iv && encryptionKeySource )
71
+ ? await decrypt ( password , key , encryptionKeySource , iv )
72
+ : password ;
73
+
65
74
const uri = mongoUri . format ( {
66
75
hosts : hosts . map ( ( host ) => ( {
67
76
host : host . split ( ":" ) [ 0 ] ,
@@ -71,9 +80,7 @@ export const getConnectionUri = (
71
80
database,
72
81
options,
73
82
username,
74
- password : ( password && key && iv )
75
- ? decrypt ( password , key , iv )
76
- : password ,
83
+ password : pwd ,
77
84
} ) ;
78
85
79
86
return uri ;
@@ -128,24 +135,53 @@ export const getReplicaSetDetails = async (
128
135
}
129
136
} ;
130
137
131
- export const encrypt = ( password : string ) => {
138
+ export const encrypt = async (
139
+ password : string ,
140
+ encryptionKey ?: string ,
141
+ encryptionKeySourceType ?: string ,
142
+ ) => {
132
143
const iv = crypto . randomBytes ( 16 ) ;
133
- const key = crypto . randomBytes ( 32 ) ;
144
+ const key = encryptionKey
145
+ ? encryptionKeySourceType === "url"
146
+ ? ( await axios . get ( encryptionKey ) ) . data . toString ( )
147
+ : ( await readFile ( encryptionKey ) ) . toString ( )
148
+ : crypto . generateKeySync ( "aes" , { length : 256 } ) ;
134
149
135
- const cipher = crypto . createCipheriv ( "aes-256-cbc" , Buffer . from ( key ) , iv ) ;
150
+ const cipherKey = encryptionKey
151
+ ? crypto . createSecretKey ( Buffer . from ( key , "hex" ) . toString ( "hex" ) , "hex" )
152
+ : key ;
153
+
154
+ const cipher = crypto . createCipheriv ( "aes-256-cbc" , cipherKey , iv ) ;
136
155
return {
137
- pwd : Buffer . concat ( [ cipher . update ( password ) , cipher . final ( ) ] ) . toString (
156
+ pwd : password && Buffer . concat ( [ cipher . update ( password ) , cipher . final ( ) ] ) . toString (
138
157
"hex"
139
158
) ,
140
- key : key . toString ( "hex" ) ,
159
+ key : encryptionKey
160
+ ? key . toString ( "hex" )
161
+ : key . export ( ) . toString ( "hex" ) ,
141
162
iv : iv . toString ( "hex" ) ,
142
163
} ;
143
164
} ;
144
165
145
- export const decrypt = ( password : string , key : string , iv : string ) => {
166
+ export const decrypt = async (
167
+ password : string ,
168
+ key : string ,
169
+ keySource : string ,
170
+ iv : string
171
+ ) => {
172
+
173
+ const encryptionKey = keySource === "url"
174
+ ? ( await axios . get ( key ) ) . data
175
+ : await readFile ( key ) ;
176
+
177
+ const secret = crypto . createSecretKey (
178
+ Buffer . from ( encryptionKey , 'hex' ) . toString ( ) ,
179
+ "hex"
180
+ ) ;
181
+
146
182
const decipher = crypto . createDecipheriv (
147
183
"aes-256-cbc" ,
148
- Buffer . from ( key , "hex" ) ,
184
+ secret ,
149
185
Buffer . from ( iv , "hex" )
150
186
) ;
151
187
return decipher . update ( password , "hex" , "utf8" ) + decipher . final ( "utf8" ) ;
@@ -186,9 +222,14 @@ export const createConnectionConfigurations = async ({
186
222
}
187
223
188
224
const encryption = parsedUri . password
189
- ? encrypt ( parsedUri . password )
225
+ ? await encrypt ( parsedUri . password )
190
226
: undefined ;
191
227
228
+ const filePath = resolve ( ARK_FOLDER_PATH , 'keys' , config . name ) ;
229
+ if ( encryption && encryption . key ) {
230
+ await writeFile ( filePath , encryption . key ) ;
231
+ }
232
+
192
233
return {
193
234
id,
194
235
protocol : parsedUri . scheme ,
@@ -197,11 +238,13 @@ export const createConnectionConfigurations = async ({
197
238
hosts : hosts ,
198
239
username : parsedUri . username ,
199
240
password : encryption ?. pwd ,
200
- key : encryption ?. key ,
241
+ key : filePath ,
201
242
iv : encryption ?. iv ,
202
243
database : parsedUri . database ,
203
244
options : { ...parsedUri . options , ...options } ,
204
245
ssh : { useSSH : false } ,
246
+ encryptionKeySource : 'generated' ,
247
+ encryptionKeySourceType : 'file'
205
248
} ;
206
249
} else {
207
250
const id = config . id || nanoid ( ) ;
@@ -222,14 +265,30 @@ export const createConnectionConfigurations = async ({
222
265
config . options = { ...opts } ;
223
266
}
224
267
225
- const encryption = config . password ? encrypt ( config . password ) : undefined ;
268
+ const encryption = config . password
269
+ ? ! config . encryptionKeySource || config . encryptionKeySource === 'generated'
270
+ ? await encrypt ( config . password )
271
+ : await encrypt ( config . password , config . key , config . encryptionKeySourceType )
272
+ : undefined ;
273
+
274
+ const filePath = config . encryptionKeySource === 'generated' && ! config . key
275
+ ? resolve ( ARK_FOLDER_PATH , 'keys' , config . name )
276
+ : config . key as string ;
226
277
227
- config . password = encryption ?. pwd ;
228
- config . key = encryption ?. key ;
229
- config . iv = encryption ?. iv ;
278
+ if ( config . encryptionKeySource === 'generated' ) {
279
+ if ( ! existsSync ( dirname ( filePath ) ) ) {
280
+ await mkdir ( dirname ( filePath ) ) ;
281
+ }
282
+ await writeFile ( filePath , encryption ?. key , { flag : 'w' } ) ;
283
+ }
230
284
231
285
return {
232
286
...config ,
287
+ password : encryption ?. pwd ,
288
+ iv : encryption ?. iv ,
289
+ key : filePath ,
290
+ encryptionKeySource : 'generated' ,
291
+ encryptionKeySourceType : 'file' ,
233
292
id,
234
293
} ;
235
294
}
0 commit comments