mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
fix(core): Assign credential ownership correctly in source control import (#8955)
This commit is contained in:
@@ -23,11 +23,12 @@ export class SharedCredentialsRepository extends Repository<SharedCredentials> {
|
||||
return sharedCredential.credentials;
|
||||
}
|
||||
|
||||
async findByCredentialIds(credentialIds: string[]) {
|
||||
async findByCredentialIds(credentialIds: string[], role: CredentialSharingRole) {
|
||||
return await this.find({
|
||||
relations: ['credentials', 'user'],
|
||||
where: {
|
||||
credentialsId: In(credentialIds),
|
||||
role,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ export class SourceControlExportService {
|
||||
const credentialIds = candidates.map((e) => e.id);
|
||||
const credentialsToBeExported = await Container.get(
|
||||
SharedCredentialsRepository,
|
||||
).findByCredentialIds(credentialIds);
|
||||
).findByCredentialIds(credentialIds, 'credential:owner');
|
||||
let missingIds: string[] = [];
|
||||
if (credentialsToBeExported.length !== credentialIds.length) {
|
||||
const foundCredentialIds = credentialsToBeExported.map((e) => e.credentialsId);
|
||||
@@ -239,23 +239,26 @@ export class SourceControlExportService {
|
||||
);
|
||||
}
|
||||
await Promise.all(
|
||||
credentialsToBeExported.map(async (sharedCredential) => {
|
||||
const { name, type, nodesAccess, data, id } = sharedCredential.credentials;
|
||||
const credentialObject = new Credentials({ id, name }, type, nodesAccess, data);
|
||||
const plainData = credentialObject.getData();
|
||||
const sanitizedData = this.replaceCredentialData(plainData);
|
||||
const fileName = this.getCredentialsPath(sharedCredential.credentials.id);
|
||||
const sanitizedCredential: ExportableCredential = {
|
||||
id: sharedCredential.credentials.id,
|
||||
name: sharedCredential.credentials.name,
|
||||
type: sharedCredential.credentials.type,
|
||||
data: sanitizedData,
|
||||
nodesAccess: sharedCredential.credentials.nodesAccess,
|
||||
credentialsToBeExported.map(async (sharing) => {
|
||||
const { name, type, nodesAccess, data, id } = sharing.credentials;
|
||||
const credentials = new Credentials({ id, name }, type, nodesAccess, data);
|
||||
|
||||
const stub: ExportableCredential = {
|
||||
id,
|
||||
name,
|
||||
type,
|
||||
data: this.replaceCredentialData(credentials.getData()),
|
||||
nodesAccess,
|
||||
ownedBy: sharing.user.email,
|
||||
};
|
||||
this.logger.debug(`Writing credential ${sharedCredential.credentials.id} to ${fileName}`);
|
||||
return await fsWriteFile(fileName, JSON.stringify(sanitizedCredential, null, 2));
|
||||
|
||||
const filePath = this.getCredentialsPath(id);
|
||||
this.logger.debug(`Writing credentials stub "${name}" (ID ${id}) to: ${filePath}`);
|
||||
|
||||
return await fsWriteFile(filePath, JSON.stringify(stub, null, 2));
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
count: credentialsToBeExported.length,
|
||||
folder: this.credentialExportFolder,
|
||||
|
||||
@@ -310,7 +310,10 @@ export class SourceControlImportService {
|
||||
}>;
|
||||
}
|
||||
|
||||
public async importCredentialsFromWorkFolder(candidates: SourceControlledFile[], userId: string) {
|
||||
public async importCredentialsFromWorkFolder(
|
||||
candidates: SourceControlledFile[],
|
||||
importingUserId: string,
|
||||
) {
|
||||
const candidateIds = candidates.map((c) => c.id);
|
||||
const existingCredentials = await Container.get(CredentialsRepository).find({
|
||||
where: {
|
||||
@@ -335,9 +338,6 @@ export class SourceControlImportService {
|
||||
const existingCredential = existingCredentials.find(
|
||||
(e) => e.id === credential.id && e.type === credential.type,
|
||||
);
|
||||
const sharedOwner = existingSharedCredentials.find(
|
||||
(e) => e.credentialsId === credential.id,
|
||||
);
|
||||
|
||||
const { name, type, data, id, nodesAccess } = credential;
|
||||
const newCredentialObject = new Credentials({ id, name }, type, []);
|
||||
@@ -351,10 +351,23 @@ export class SourceControlImportService {
|
||||
this.logger.debug(`Updating credential id ${newCredentialObject.id as string}`);
|
||||
await Container.get(CredentialsRepository).upsert(newCredentialObject, ['id']);
|
||||
|
||||
if (!sharedOwner) {
|
||||
const isOwnedLocally = existingSharedCredentials.some(
|
||||
(c) => c.credentialsId === credential.id,
|
||||
);
|
||||
|
||||
if (!isOwnedLocally) {
|
||||
const remoteOwnerId = credential.ownedBy
|
||||
? await Container.get(UserRepository)
|
||||
.findOne({
|
||||
where: { email: credential.ownedBy },
|
||||
select: { id: true },
|
||||
})
|
||||
.then((user) => user?.id)
|
||||
: null;
|
||||
|
||||
const newSharedCredential = new SharedCredentials();
|
||||
newSharedCredential.credentialsId = newCredentialObject.id as string;
|
||||
newSharedCredential.userId = userId;
|
||||
newSharedCredential.userId = remoteOwnerId ?? importingUserId;
|
||||
newSharedCredential.role = 'credential:owner';
|
||||
|
||||
await Container.get(SharedCredentialsRepository).upsert({ ...newSharedCredential }, [
|
||||
|
||||
@@ -6,4 +6,10 @@ export interface ExportableCredential {
|
||||
type: string;
|
||||
data: ICredentialDataDecryptedObject;
|
||||
nodesAccess: ICredentialNodeAccess[];
|
||||
|
||||
/**
|
||||
* Email of the user who owns this credential at the source instance.
|
||||
* Ownership is mirrored at target instance if user is also present there.
|
||||
*/
|
||||
ownedBy: string | null;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ export class SourceControlPullWorkFolder {
|
||||
}
|
||||
|
||||
export class SourceControllPullOptions {
|
||||
/** ID of user performing a source control pull. */
|
||||
userId: string;
|
||||
|
||||
force?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user