@@ -64,67 +64,25 @@ export class ImportCredentialsCommand extends BaseCommand {
64
64
}
65
65
}
66
66
67
- let totalImported = 0 ;
68
-
69
- const cipher = Container . get ( Cipher ) ;
70
67
const user = flags . userId ? await this . getAssignee ( flags . userId ) : await this . getOwner ( ) ;
71
68
72
- if ( flags . separate ) {
73
- let { input : inputPath } = flags ;
74
-
75
- if ( process . platform === 'win32' ) {
76
- inputPath = inputPath . replace ( / \\ / g, '/' ) ;
77
- }
78
-
79
- const files = await glob ( '*.json' , {
80
- cwd : inputPath ,
81
- absolute : true ,
82
- } ) ;
83
-
84
- totalImported = files . length ;
85
-
86
- await Db . getConnection ( ) . transaction ( async ( transactionManager ) => {
87
- this . transactionManager = transactionManager ;
88
- for ( const file of files ) {
89
- const credential = jsonParse < ICredentialsEncrypted > (
90
- fs . readFileSync ( file , { encoding : 'utf8' } ) ,
91
- ) ;
92
- if ( typeof credential . data === 'object' ) {
93
- // plain data / decrypted input. Should be encrypted first.
94
- credential . data = cipher . encrypt ( credential . data ) ;
95
- }
96
- await this . storeCredential ( credential , user ) ;
97
- }
98
- } ) ;
69
+ const credentials = await this . readCredentials ( flags . input , flags . separate ) ;
99
70
100
- this . reportSuccess ( totalImported ) ;
101
- return ;
102
- }
103
-
104
- const credentials = jsonParse < ICredentialsEncrypted [ ] > (
105
- fs . readFileSync ( flags . input , { encoding : 'utf8' } ) ,
106
- ) ;
71
+ await Db . getConnection ( ) . transaction ( async ( transactionManager ) => {
72
+ this . transactionManager = transactionManager ;
107
73
108
- totalImported = credentials . length ;
74
+ const result = await this . checkRelations ( credentials , flags . userId ) ;
109
75
110
- if ( ! Array . isArray ( credentials ) ) {
111
- throw new ApplicationError (
112
- 'File does not seem to contain credentials. Make sure the credentials are contained in an array.' ,
113
- ) ;
114
- }
76
+ if ( ! result . success ) {
77
+ throw new ApplicationError ( result . message ) ;
78
+ }
115
79
116
- await Db . getConnection ( ) . transaction ( async ( transactionManager ) => {
117
- this . transactionManager = transactionManager ;
118
80
for ( const credential of credentials ) {
119
- if ( typeof credential . data === 'object' ) {
120
- // plain data / decrypted input. Should be encrypted first.
121
- credential . data = cipher . encrypt ( credential . data ) ;
122
- }
123
81
await this . storeCredential ( credential , user ) ;
124
82
}
125
83
} ) ;
126
84
127
- this . reportSuccess ( totalImported ) ;
85
+ this . reportSuccess ( credentials . length ) ;
128
86
}
129
87
130
88
async catch ( error : Error ) {
@@ -145,15 +103,23 @@ export class ImportCredentialsCommand extends BaseCommand {
145
103
credential . nodesAccess = [ ] ;
146
104
}
147
105
const result = await this . transactionManager . upsert ( CredentialsEntity , credential , [ 'id' ] ) ;
148
- await this . transactionManager . upsert (
149
- SharedCredentials ,
150
- {
151
- credentialsId : result . identifiers [ 0 ] . id as string ,
152
- userId : user . id ,
153
- role : 'credential:owner' ,
154
- } ,
155
- [ 'credentialsId' , 'userId' ] ,
156
- ) ;
106
+
107
+ const sharingExists = await this . transactionManager . existsBy ( SharedCredentials , {
108
+ credentialsId : credential . id ,
109
+ role : 'credential:owner' ,
110
+ } ) ;
111
+
112
+ if ( ! sharingExists ) {
113
+ await this . transactionManager . upsert (
114
+ SharedCredentials ,
115
+ {
116
+ credentialsId : result . identifiers [ 0 ] . id as string ,
117
+ userId : user . id ,
118
+ role : 'credential:owner' ,
119
+ } ,
120
+ [ 'credentialsId' , 'userId' ] ,
121
+ ) ;
122
+ }
157
123
}
158
124
159
125
private async getOwner ( ) {
@@ -165,6 +131,84 @@ export class ImportCredentialsCommand extends BaseCommand {
165
131
return owner ;
166
132
}
167
133
134
+ private async checkRelations ( credentials : ICredentialsEncrypted [ ] , userId ?: string ) {
135
+ if ( ! userId ) {
136
+ return {
137
+ success : true as const ,
138
+ message : undefined ,
139
+ } ;
140
+ }
141
+
142
+ for ( const credential of credentials ) {
143
+ if ( credential . id === undefined ) {
144
+ continue ;
145
+ }
146
+
147
+ if ( ! ( await this . credentialExists ( credential . id ) ) ) {
148
+ continue ;
149
+ }
150
+
151
+ const ownerId = await this . getCredentialOwner ( credential . id ) ;
152
+ if ( ! ownerId ) {
153
+ continue ;
154
+ }
155
+
156
+ if ( ownerId !== userId ) {
157
+ return {
158
+ success : false as const ,
159
+ message : `The credential with id "${ credential . id } " is already owned by the user with the id "${ ownerId } ". It can't be re-owned by the user with the id "${ userId } "` ,
160
+ } ;
161
+ }
162
+ }
163
+
164
+ return {
165
+ success : true as const ,
166
+ message : undefined ,
167
+ } ;
168
+ }
169
+
170
+ private async readCredentials ( path : string , separate : boolean ) : Promise < ICredentialsEncrypted [ ] > {
171
+ const cipher = Container . get ( Cipher ) ;
172
+
173
+ if ( process . platform === 'win32' ) {
174
+ path = path . replace ( / \\ / g, '/' ) ;
175
+ }
176
+
177
+ let credentials : ICredentialsEncrypted [ ] ;
178
+
179
+ if ( separate ) {
180
+ const files = await glob ( '*.json' , {
181
+ cwd : path ,
182
+ absolute : true ,
183
+ } ) ;
184
+
185
+ credentials = files . map ( ( file ) =>
186
+ jsonParse < ICredentialsEncrypted > ( fs . readFileSync ( file , { encoding : 'utf8' } ) ) ,
187
+ ) ;
188
+ } else {
189
+ const credentialsUnchecked = jsonParse < ICredentialsEncrypted [ ] > (
190
+ fs . readFileSync ( path , { encoding : 'utf8' } ) ,
191
+ ) ;
192
+
193
+ if ( ! Array . isArray ( credentialsUnchecked ) ) {
194
+ throw new ApplicationError (
195
+ 'File does not seem to contain credentials. Make sure the credentials are contained in an array.' ,
196
+ ) ;
197
+ }
198
+
199
+ credentials = credentialsUnchecked ;
200
+ }
201
+
202
+ return credentials . map ( ( credential ) => {
203
+ if ( typeof credential . data === 'object' ) {
204
+ // plain data / decrypted input. Should be encrypted first.
205
+ credential . data = cipher . encrypt ( credential . data ) ;
206
+ }
207
+
208
+ return credential ;
209
+ } ) ;
210
+ }
211
+
168
212
private async getAssignee ( userId : string ) {
169
213
const user = await Container . get ( UserRepository ) . findOneBy ( { id : userId } ) ;
170
214
@@ -174,4 +218,17 @@ export class ImportCredentialsCommand extends BaseCommand {
174
218
175
219
return user ;
176
220
}
221
+
222
+ private async getCredentialOwner ( credentialsId : string ) {
223
+ const sharedCredential = await this . transactionManager . findOneBy ( SharedCredentials , {
224
+ credentialsId,
225
+ role : 'credential:owner' ,
226
+ } ) ;
227
+
228
+ return sharedCredential ?. userId ;
229
+ }
230
+
231
+ private async credentialExists ( credentialId : string ) {
232
+ return await this . transactionManager . existsBy ( CredentialsEntity , { id : credentialId } ) ;
233
+ }
177
234
}
0 commit comments