2024-05-17 01:53:15 -07:00
|
|
|
import { In, type EntityManager } from '@n8n/typeorm';
|
2023-01-27 05:56:56 -08:00
|
|
|
import type { User } from '@db/entities/User';
|
2024-05-17 01:53:15 -07:00
|
|
|
import { CredentialsService } from './credentials.service';
|
2023-11-10 06:04:26 -08:00
|
|
|
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
2024-05-17 01:53:15 -07:00
|
|
|
import type { CredentialsEntity } from '@/databases/entities/CredentialsEntity';
|
2024-01-31 00:48:48 -08:00
|
|
|
import { Service } from 'typedi';
|
2024-05-17 01:53:15 -07:00
|
|
|
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
|
|
|
|
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
|
|
|
import { OwnershipService } from '@/services/ownership.service';
|
|
|
|
import { Project } from '@/databases/entities/Project';
|
2022-09-21 01:20:29 -07:00
|
|
|
|
2024-01-31 00:48:48 -08:00
|
|
|
@Service()
|
|
|
|
export class EnterpriseCredentialsService {
|
|
|
|
constructor(
|
|
|
|
private readonly sharedCredentialsRepository: SharedCredentialsRepository,
|
2024-05-17 01:53:15 -07:00
|
|
|
private readonly ownershipService: OwnershipService,
|
|
|
|
private readonly credentialsService: CredentialsService,
|
2024-01-31 00:48:48 -08:00
|
|
|
) {}
|
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
async shareWithProjects(
|
|
|
|
credential: CredentialsEntity,
|
|
|
|
shareWithIds: string[],
|
|
|
|
entityManager?: EntityManager,
|
|
|
|
) {
|
|
|
|
const em = entityManager ?? this.sharedCredentialsRepository.manager;
|
2022-09-21 01:20:29 -07:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
const projects = await em.find(Project, {
|
|
|
|
where: { id: In(shareWithIds), type: 'personal' },
|
|
|
|
});
|
2022-09-21 01:20:29 -07:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
const newSharedCredentials = projects
|
|
|
|
// We filter by role === 'project:personalOwner' above and there should
|
|
|
|
// always only be one owner.
|
|
|
|
.map((project) =>
|
|
|
|
this.sharedCredentialsRepository.create({
|
|
|
|
credentialsId: credential.id,
|
|
|
|
role: 'credential:user',
|
|
|
|
projectId: project.id,
|
|
|
|
}),
|
|
|
|
);
|
2022-09-21 01:20:29 -07:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
return await em.save(newSharedCredentials);
|
2022-09-21 01:20:29 -07:00
|
|
|
}
|
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
async getOne(user: User, credentialId: string, includeDecryptedData: boolean) {
|
|
|
|
let credential: CredentialsEntity | null = null;
|
|
|
|
let decryptedData: ICredentialDataDecryptedObject | null = null;
|
2022-11-21 23:37:52 -08:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
credential = includeDecryptedData
|
|
|
|
? // Try to get the credential with `credential:update` scope, which
|
|
|
|
// are required for decrypting the data.
|
|
|
|
await this.sharedCredentialsRepository.findCredentialForUser(
|
|
|
|
credentialId,
|
|
|
|
user,
|
|
|
|
// TODO: replace credential:update with credential:decrypt once it lands
|
|
|
|
// see: https://n8nio.slack.com/archives/C062YRE7EG4/p1708531433206069?thread_ts=1708525972.054149&cid=C062YRE7EG4
|
|
|
|
['credential:read', 'credential:update'],
|
|
|
|
)
|
|
|
|
: null;
|
2022-11-21 23:37:52 -08:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
if (credential) {
|
|
|
|
// Decrypt the data if we found the credential with the `credential:update`
|
|
|
|
// scope.
|
|
|
|
decryptedData = this.credentialsService.redact(
|
|
|
|
this.credentialsService.decrypt(credential),
|
|
|
|
credential,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
// Otherwise try to find them with only the `credential:read` scope. In
|
|
|
|
// that case we return them without the decrypted data.
|
|
|
|
credential = await this.sharedCredentialsRepository.findCredentialForUser(
|
|
|
|
credentialId,
|
|
|
|
user,
|
|
|
|
['credential:read'],
|
|
|
|
);
|
|
|
|
}
|
2022-11-21 23:37:52 -08:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
if (!credential) {
|
|
|
|
throw new NotFoundError(
|
|
|
|
'Could not load the credential. If you think this is an error, ask the owner to share it with you again',
|
|
|
|
);
|
|
|
|
}
|
2024-01-31 00:48:48 -08:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
credential = this.ownershipService.addOwnedByAndSharedWith(credential);
|
2022-09-21 01:20:29 -07:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
const { data: _, ...rest } = credential;
|
2022-09-21 01:20:29 -07:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
if (decryptedData) {
|
|
|
|
return { data: decryptedData, ...rest };
|
|
|
|
}
|
2022-09-21 01:20:29 -07:00
|
|
|
|
2024-05-17 01:53:15 -07:00
|
|
|
return { ...rest };
|
2022-09-21 01:20:29 -07:00
|
|
|
}
|
|
|
|
}
|