Skip to content

Commit 910a1aa

Browse files
committed
don't allow to create multiple owners for the same credential
1 parent a3b5984 commit 910a1aa

File tree

6 files changed

+392
-10
lines changed

6 files changed

+392
-10
lines changed

packages/cli/src/commands/import/credentials.ts

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type { ICredentialsEncrypted } from 'n8n-workflow';
1515
import { ApplicationError, jsonParse } from 'n8n-workflow';
1616
import { UM_FIX_INSTRUCTION } from '@/constants';
1717
import { UserRepository } from '@db/repositories/user.repository';
18+
import { SharedCredentialsRepository } from '@/databases/repositories/sharedCredentials.repository';
1819

1920
export class ImportCredentialsCommand extends BaseCommand {
2021
static description = 'Import credentials';
@@ -89,6 +90,17 @@ export class ImportCredentialsCommand extends BaseCommand {
8990
const credential = jsonParse<ICredentialsEncrypted>(
9091
fs.readFileSync(file, { encoding: 'utf8' }),
9192
);
93+
94+
if (credential.id && flags.userId) {
95+
const credentialOwner = await this.getCredentialOwner(credential.id);
96+
97+
if (credentialOwner && credentialOwner !== flags.userId) {
98+
throw new ApplicationError(
99+
`The credential with id "${credential.id}" is already owned by the user with the id "${credentialOwner}". It can't be re-owned by the user with the id "${flags.userId}"`,
100+
);
101+
}
102+
}
103+
92104
if (typeof credential.data === 'object') {
93105
// plain data / decrypted input. Should be encrypted first.
94106
credential.data = cipher.encrypt(credential.data);
@@ -116,6 +128,15 @@ export class ImportCredentialsCommand extends BaseCommand {
116128
await Db.getConnection().transaction(async (transactionManager) => {
117129
this.transactionManager = transactionManager;
118130
for (const credential of credentials) {
131+
if (credential.id && flags.userId) {
132+
const credentialOwner = await this.getCredentialOwner(credential.id);
133+
if (credentialOwner && credentialOwner !== flags.userId) {
134+
throw new ApplicationError(
135+
`The credential with id "${credential.id}" is already owned by the user with the id "${credentialOwner}". It can't be re-owned by the user with the id "${flags.userId}"`,
136+
);
137+
}
138+
}
139+
119140
if (typeof credential.data === 'object') {
120141
// plain data / decrypted input. Should be encrypted first.
121142
credential.data = cipher.encrypt(credential.data);
@@ -142,15 +163,23 @@ export class ImportCredentialsCommand extends BaseCommand {
142163

143164
private async storeCredential(credential: Partial<CredentialsEntity>, user: User) {
144165
const result = await this.transactionManager.upsert(CredentialsEntity, credential, ['id']);
145-
await this.transactionManager.upsert(
146-
SharedCredentials,
147-
{
148-
credentialsId: result.identifiers[0].id as string,
149-
userId: user.id,
150-
role: 'credential:owner',
151-
},
152-
['credentialsId', 'userId'],
153-
);
166+
167+
const sharingExists = await this.transactionManager.existsBy(SharedCredentials, {
168+
credentialsId: credential.id,
169+
role: 'credential:owner',
170+
});
171+
172+
if (!sharingExists) {
173+
await this.transactionManager.upsert(
174+
SharedCredentials,
175+
{
176+
credentialsId: result.identifiers[0].id as string,
177+
userId: user.id,
178+
role: 'credential:owner',
179+
},
180+
['credentialsId', 'userId'],
181+
);
182+
}
154183
}
155184

156185
private async getOwner() {
@@ -171,4 +200,13 @@ export class ImportCredentialsCommand extends BaseCommand {
171200

172201
return user;
173202
}
203+
204+
private async getCredentialOwner(credentialsId: string) {
205+
const sharedCredential = await Container.get(SharedCredentialsRepository).findOneBy({
206+
credentialsId,
207+
role: 'credential:owner',
208+
});
209+
210+
return sharedCredential?.userId;
211+
}
174212
}

0 commit comments

Comments
 (0)