mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
refactor(core, editor): Remove legacy nodesAccess
(no-changelog) (#9016)
This commit is contained in:
parent
ba986fb018
commit
b8ab049932
|
@ -268,7 +268,6 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||||
return new Credentials(
|
return new Credentials(
|
||||||
{ id: credential.id, name: credential.name },
|
{ id: credential.id, name: credential.name },
|
||||||
credential.type,
|
credential.type,
|
||||||
credential.nodesAccess,
|
|
||||||
credential.data,
|
credential.data,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -483,9 +482,9 @@ export function createCredentialsFromCredentialsEntity(
|
||||||
credential: CredentialsEntity,
|
credential: CredentialsEntity,
|
||||||
encrypt = false,
|
encrypt = false,
|
||||||
): Credentials {
|
): Credentials {
|
||||||
const { id, name, type, nodesAccess, data } = credential;
|
const { id, name, type, data } = credential;
|
||||||
if (encrypt) {
|
if (encrypt) {
|
||||||
return new Credentials({ id: null, name }, type, nodesAccess);
|
return new Credentials({ id: null, name }, type);
|
||||||
}
|
}
|
||||||
return new Credentials({ id, name }, type, nodesAccess, data);
|
return new Credentials({ id, name }, type, data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,20 +41,6 @@ export async function createCredential(
|
||||||
|
|
||||||
Object.assign(newCredential, properties);
|
Object.assign(newCredential, properties);
|
||||||
|
|
||||||
if (!newCredential.nodesAccess || newCredential.nodesAccess.length === 0) {
|
|
||||||
newCredential.nodesAccess = [
|
|
||||||
{
|
|
||||||
nodeType: `n8n-nodes-base.${properties.type?.toLowerCase() ?? 'unknown'}`,
|
|
||||||
date: new Date(),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
// Add the added date for node access permissions
|
|
||||||
newCredential.nodesAccess.forEach((nodeAccess) => {
|
|
||||||
nodeAccess.date = new Date();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return newCredential;
|
return newCredential;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,11 +77,7 @@ export async function removeCredential(credentials: CredentialsEntity): Promise<
|
||||||
|
|
||||||
export async function encryptCredential(credential: CredentialsEntity): Promise<ICredentialsDb> {
|
export async function encryptCredential(credential: CredentialsEntity): Promise<ICredentialsDb> {
|
||||||
// Encrypt the data
|
// Encrypt the data
|
||||||
const coreCredential = new Credentials(
|
const coreCredential = new Credentials({ id: null, name: credential.name }, credential.type);
|
||||||
{ id: null, name: credential.name },
|
|
||||||
credential.type,
|
|
||||||
credential.nodesAccess,
|
|
||||||
);
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
coreCredential.setData(credential.data);
|
coreCredential.setData(credential.data);
|
||||||
|
@ -115,7 +97,7 @@ export function sanitizeCredentials(
|
||||||
const credentialsList = argIsArray ? credentials : [credentials];
|
const credentialsList = argIsArray ? credentials : [credentials];
|
||||||
|
|
||||||
const sanitizedCredentials = credentialsList.map((credential) => {
|
const sanitizedCredentials = credentialsList.map((credential) => {
|
||||||
const { data, nodesAccess, shared, ...rest } = credential;
|
const { data, shared, ...rest } = credential;
|
||||||
return rest;
|
return rest;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -111,9 +111,9 @@ export class ExportCredentialsCommand extends BaseCommand {
|
||||||
|
|
||||||
if (flags.decrypted) {
|
if (flags.decrypted) {
|
||||||
for (let i = 0; i < credentials.length; i++) {
|
for (let i = 0; i < credentials.length; i++) {
|
||||||
const { name, type, nodesAccess, data } = credentials[i];
|
const { name, type, data } = credentials[i];
|
||||||
const id = credentials[i].id;
|
const id = credentials[i].id;
|
||||||
const credential = new Credentials({ id, name }, type, nodesAccess, data);
|
const credential = new Credentials({ id, name }, type, data);
|
||||||
const plainData = credential.getData();
|
const plainData = credential.getData();
|
||||||
(credentials[i] as ICredentialsDecryptedDb).data = plainData;
|
(credentials[i] as ICredentialsDecryptedDb).data = plainData;
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,9 +141,6 @@ export class ImportCredentialsCommand extends BaseCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async storeCredential(credential: Partial<CredentialsEntity>, user: User) {
|
private async storeCredential(credential: Partial<CredentialsEntity>, user: User) {
|
||||||
if (!credential.nodesAccess) {
|
|
||||||
credential.nodesAccess = [];
|
|
||||||
}
|
|
||||||
const result = await this.transactionManager.upsert(CredentialsEntity, credential, ['id']);
|
const result = await this.transactionManager.upsert(CredentialsEntity, credential, ['id']);
|
||||||
await this.transactionManager.upsert(
|
await this.transactionManager.upsert(
|
||||||
SharedCredentials,
|
SharedCredentials,
|
||||||
|
|
|
@ -95,7 +95,7 @@ export abstract class AbstractOAuthController {
|
||||||
credential: ICredentialsDb,
|
credential: ICredentialsDb,
|
||||||
decryptedData: ICredentialDataDecryptedObject,
|
decryptedData: ICredentialDataDecryptedObject,
|
||||||
) {
|
) {
|
||||||
const credentials = new Credentials(credential, credential.type, credential.nodesAccess);
|
const credentials = new Credentials(credential, credential.type);
|
||||||
credentials.setData(decryptedData);
|
credentials.setData(decryptedData);
|
||||||
await this.credentialsRepository.update(credential.id, {
|
await this.credentialsRepository.update(credential.id, {
|
||||||
...credentials.getDataToSave(),
|
...credentials.getDataToSave(),
|
||||||
|
|
|
@ -109,11 +109,6 @@ export class CredentialsService {
|
||||||
|
|
||||||
await validateEntity(newCredentials);
|
await validateEntity(newCredentials);
|
||||||
|
|
||||||
// Add the date for newly added node access permissions
|
|
||||||
for (const nodeAccess of newCredentials.nodesAccess) {
|
|
||||||
nodeAccess.date = new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
return newCredentials;
|
return newCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,13 +127,6 @@ export class CredentialsService {
|
||||||
|
|
||||||
await validateEntity(updateData);
|
await validateEntity(updateData);
|
||||||
|
|
||||||
// Add the date for newly added node access permissions
|
|
||||||
for (const nodeAccess of updateData.nodesAccess) {
|
|
||||||
if (!nodeAccess.date) {
|
|
||||||
nodeAccess.date = new Date();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not overwrite the oauth data else data like the access or refresh token would get lost
|
// Do not overwrite the oauth data else data like the access or refresh token would get lost
|
||||||
// everytime anybody changes anything on the credentials even if it is just the name.
|
// everytime anybody changes anything on the credentials even if it is just the name.
|
||||||
if (decryptedData.oauthTokenData) {
|
if (decryptedData.oauthTokenData) {
|
||||||
|
@ -149,11 +137,7 @@ export class CredentialsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
createEncryptedData(credentialId: string | null, data: CredentialsEntity): ICredentialsDb {
|
createEncryptedData(credentialId: string | null, data: CredentialsEntity): ICredentialsDb {
|
||||||
const credentials = new Credentials(
|
const credentials = new Credentials({ id: credentialId, name: data.name }, data.type);
|
||||||
{ id: credentialId, name: data.name },
|
|
||||||
data.type,
|
|
||||||
data.nodesAccess,
|
|
||||||
);
|
|
||||||
|
|
||||||
credentials.setData(data.data as unknown as ICredentialDataDecryptedObject);
|
credentials.setData(data.data as unknown as ICredentialDataDecryptedObject);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import type { ICredentialNodeAccess } from 'n8n-workflow';
|
|
||||||
import { Column, Entity, Index, OneToMany } from '@n8n/typeorm';
|
import { Column, Entity, Index, OneToMany } from '@n8n/typeorm';
|
||||||
import { IsArray, IsObject, IsString, Length } from 'class-validator';
|
import { IsObject, IsString, Length } from 'class-validator';
|
||||||
import type { SharedCredentials } from './SharedCredentials';
|
import type { SharedCredentials } from './SharedCredentials';
|
||||||
import { WithTimestampsAndStringId, jsonColumnType } from './AbstractEntity';
|
import { WithTimestampsAndStringId } from './AbstractEntity';
|
||||||
import type { ICredentialsDb } from '@/Interfaces';
|
import type { ICredentialsDb } from '@/Interfaces';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
|
@ -27,8 +26,4 @@ export class CredentialsEntity extends WithTimestampsAndStringId implements ICre
|
||||||
|
|
||||||
@OneToMany('SharedCredentials', 'credentials')
|
@OneToMany('SharedCredentials', 'credentials')
|
||||||
shared: SharedCredentials[];
|
shared: SharedCredentials[];
|
||||||
|
|
||||||
@Column(jsonColumnType)
|
|
||||||
@IsArray()
|
|
||||||
nodesAccess: ICredentialNodeAccess[];
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import type { IrreversibleMigration, MigrationContext } from '@db/types';
|
||||||
|
|
||||||
|
export class RemoveNodesAccess1712044305787 implements IrreversibleMigration {
|
||||||
|
async up({ schemaBuilder: { dropColumns } }: MigrationContext) {
|
||||||
|
await dropColumns('credentials_entity', ['nodesAccess']);
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,6 +54,7 @@ import { AddGlobalAdminRole1700571993961 } from '../common/1700571993961-AddGlob
|
||||||
import { DropRoleMapping1705429061930 } from '../common/1705429061930-DropRoleMapping';
|
import { DropRoleMapping1705429061930 } from '../common/1705429061930-DropRoleMapping';
|
||||||
import { RemoveFailedExecutionStatus1711018413374 } from '../common/1711018413374-RemoveFailedExecutionStatus';
|
import { RemoveFailedExecutionStatus1711018413374 } from '../common/1711018413374-RemoveFailedExecutionStatus';
|
||||||
import { MoveSshKeysToDatabase1711390882123 } from '../common/1711390882123-MoveSshKeysToDatabase';
|
import { MoveSshKeysToDatabase1711390882123 } from '../common/1711390882123-MoveSshKeysToDatabase';
|
||||||
|
import { RemoveNodesAccess1712044305787 } from '../common/1712044305787-RemoveNodesAccess';
|
||||||
|
|
||||||
export const mysqlMigrations: Migration[] = [
|
export const mysqlMigrations: Migration[] = [
|
||||||
InitialMigration1588157391238,
|
InitialMigration1588157391238,
|
||||||
|
@ -111,4 +112,5 @@ export const mysqlMigrations: Migration[] = [
|
||||||
DropRoleMapping1705429061930,
|
DropRoleMapping1705429061930,
|
||||||
RemoveFailedExecutionStatus1711018413374,
|
RemoveFailedExecutionStatus1711018413374,
|
||||||
MoveSshKeysToDatabase1711390882123,
|
MoveSshKeysToDatabase1711390882123,
|
||||||
|
RemoveNodesAccess1712044305787,
|
||||||
];
|
];
|
||||||
|
|
|
@ -53,6 +53,7 @@ import { AddGlobalAdminRole1700571993961 } from '../common/1700571993961-AddGlob
|
||||||
import { DropRoleMapping1705429061930 } from '../common/1705429061930-DropRoleMapping';
|
import { DropRoleMapping1705429061930 } from '../common/1705429061930-DropRoleMapping';
|
||||||
import { RemoveFailedExecutionStatus1711018413374 } from '../common/1711018413374-RemoveFailedExecutionStatus';
|
import { RemoveFailedExecutionStatus1711018413374 } from '../common/1711018413374-RemoveFailedExecutionStatus';
|
||||||
import { MoveSshKeysToDatabase1711390882123 } from '../common/1711390882123-MoveSshKeysToDatabase';
|
import { MoveSshKeysToDatabase1711390882123 } from '../common/1711390882123-MoveSshKeysToDatabase';
|
||||||
|
import { RemoveNodesAccess1712044305787 } from '../common/1712044305787-RemoveNodesAccess';
|
||||||
|
|
||||||
export const postgresMigrations: Migration[] = [
|
export const postgresMigrations: Migration[] = [
|
||||||
InitialMigration1587669153312,
|
InitialMigration1587669153312,
|
||||||
|
@ -109,4 +110,5 @@ export const postgresMigrations: Migration[] = [
|
||||||
DropRoleMapping1705429061930,
|
DropRoleMapping1705429061930,
|
||||||
RemoveFailedExecutionStatus1711018413374,
|
RemoveFailedExecutionStatus1711018413374,
|
||||||
MoveSshKeysToDatabase1711390882123,
|
MoveSshKeysToDatabase1711390882123,
|
||||||
|
RemoveNodesAccess1712044305787,
|
||||||
];
|
];
|
||||||
|
|
|
@ -51,6 +51,7 @@ import { AddGlobalAdminRole1700571993961 } from '../common/1700571993961-AddGlob
|
||||||
import { DropRoleMapping1705429061930 } from './1705429061930-DropRoleMapping';
|
import { DropRoleMapping1705429061930 } from './1705429061930-DropRoleMapping';
|
||||||
import { RemoveFailedExecutionStatus1711018413374 } from '../common/1711018413374-RemoveFailedExecutionStatus';
|
import { RemoveFailedExecutionStatus1711018413374 } from '../common/1711018413374-RemoveFailedExecutionStatus';
|
||||||
import { MoveSshKeysToDatabase1711390882123 } from '../common/1711390882123-MoveSshKeysToDatabase';
|
import { MoveSshKeysToDatabase1711390882123 } from '../common/1711390882123-MoveSshKeysToDatabase';
|
||||||
|
import { RemoveNodesAccess1712044305787 } from '../common/1712044305787-RemoveNodesAccess';
|
||||||
|
|
||||||
const sqliteMigrations: Migration[] = [
|
const sqliteMigrations: Migration[] = [
|
||||||
InitialMigration1588102412422,
|
InitialMigration1588102412422,
|
||||||
|
@ -105,6 +106,7 @@ const sqliteMigrations: Migration[] = [
|
||||||
DropRoleMapping1705429061930,
|
DropRoleMapping1705429061930,
|
||||||
RemoveFailedExecutionStatus1711018413374,
|
RemoveFailedExecutionStatus1711018413374,
|
||||||
MoveSshKeysToDatabase1711390882123,
|
MoveSshKeysToDatabase1711390882123,
|
||||||
|
RemoveNodesAccess1712044305787,
|
||||||
];
|
];
|
||||||
|
|
||||||
export { sqliteMigrations };
|
export { sqliteMigrations };
|
||||||
|
|
|
@ -46,7 +46,7 @@ export class CredentialsRepository extends Repository<CredentialsEntity> {
|
||||||
type Select = Array<keyof CredentialsEntity>;
|
type Select = Array<keyof CredentialsEntity>;
|
||||||
|
|
||||||
const defaultRelations = ['shared', 'shared.user'];
|
const defaultRelations = ['shared', 'shared.user'];
|
||||||
const defaultSelect: Select = ['id', 'name', 'type', 'nodesAccess', 'createdAt', 'updatedAt'];
|
const defaultSelect: Select = ['id', 'name', 'type', 'createdAt', 'updatedAt'];
|
||||||
|
|
||||||
if (!listQueryOptions) return { select: defaultSelect, relations: defaultRelations };
|
if (!listQueryOptions) return { select: defaultSelect, relations: defaultRelations };
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import { SourceControlPreferencesService } from './sourceControlPreferences.serv
|
||||||
import { writeFileSync } from 'fs';
|
import { writeFileSync } from 'fs';
|
||||||
import { SourceControlImportService } from './sourceControlImport.service.ee';
|
import { SourceControlImportService } from './sourceControlImport.service.ee';
|
||||||
import type { User } from '@db/entities/User';
|
import type { User } from '@db/entities/User';
|
||||||
import isEqual from 'lodash/isEqual';
|
|
||||||
import type { SourceControlGetStatus } from './types/sourceControlGetStatus';
|
import type { SourceControlGetStatus } from './types/sourceControlGetStatus';
|
||||||
import type { TagEntity } from '@db/entities/TagEntity';
|
import type { TagEntity } from '@db/entities/TagEntity';
|
||||||
import type { Variables } from '@db/entities/Variables';
|
import type { Variables } from '@db/entities/Variables';
|
||||||
|
@ -384,7 +383,7 @@ export class SourceControlService {
|
||||||
* Does a comparison between the local and remote workfolder based on NOT the git status,
|
* Does a comparison between the local and remote workfolder based on NOT the git status,
|
||||||
* but certain parameters within the items being synced.
|
* but certain parameters within the items being synced.
|
||||||
* For workflows, it compares the versionIds
|
* For workflows, it compares the versionIds
|
||||||
* For credentials, it compares the name, type and nodeAccess
|
* For credentials, it compares the name and type
|
||||||
* For variables, it compares the name
|
* For variables, it compares the name
|
||||||
* For tags, it compares the name and mapping
|
* For tags, it compares the name and mapping
|
||||||
* @returns either SourceControlledFile[] if verbose is false,
|
* @returns either SourceControlledFile[] if verbose is false,
|
||||||
|
@ -565,12 +564,7 @@ export class SourceControlService {
|
||||||
> = [];
|
> = [];
|
||||||
credLocalIds.forEach((local) => {
|
credLocalIds.forEach((local) => {
|
||||||
const mismatchingCreds = credRemoteIds.find((remote) => {
|
const mismatchingCreds = credRemoteIds.find((remote) => {
|
||||||
return (
|
return remote.id === local.id && (remote.name !== local.name || remote.type !== local.type);
|
||||||
remote.id === local.id &&
|
|
||||||
(remote.name !== local.name ||
|
|
||||||
remote.type !== local.type ||
|
|
||||||
!isEqual(remote.nodesAccess, local.nodesAccess))
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
if (mismatchingCreds) {
|
if (mismatchingCreds) {
|
||||||
credModifiedInEither.push({
|
credModifiedInEither.push({
|
||||||
|
|
|
@ -240,15 +240,14 @@ export class SourceControlExportService {
|
||||||
}
|
}
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
credentialsToBeExported.map(async (sharing) => {
|
credentialsToBeExported.map(async (sharing) => {
|
||||||
const { name, type, nodesAccess, data, id } = sharing.credentials;
|
const { name, type, data, id } = sharing.credentials;
|
||||||
const credentials = new Credentials({ id, name }, type, nodesAccess, data);
|
const credentials = new Credentials({ id, name }, type, data);
|
||||||
|
|
||||||
const stub: ExportableCredential = {
|
const stub: ExportableCredential = {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
type,
|
type,
|
||||||
data: this.replaceCredentialData(credentials.getData()),
|
data: this.replaceCredentialData(credentials.getData()),
|
||||||
nodesAccess,
|
|
||||||
ownedBy: sharing.user.email,
|
ownedBy: sharing.user.email,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -142,13 +142,12 @@ export class SourceControlImportService {
|
||||||
Array<ExportableCredential & { filename: string }>
|
Array<ExportableCredential & { filename: string }>
|
||||||
> {
|
> {
|
||||||
const localCredentials = await Container.get(CredentialsRepository).find({
|
const localCredentials = await Container.get(CredentialsRepository).find({
|
||||||
select: ['id', 'name', 'type', 'nodesAccess'],
|
select: ['id', 'name', 'type'],
|
||||||
});
|
});
|
||||||
return localCredentials.map((local) => ({
|
return localCredentials.map((local) => ({
|
||||||
id: local.id,
|
id: local.id,
|
||||||
name: local.name,
|
name: local.name,
|
||||||
type: local.type,
|
type: local.type,
|
||||||
nodesAccess: local.nodesAccess,
|
|
||||||
filename: getCredentialExportPath(local.id, this.credentialExportFolder),
|
filename: getCredentialExportPath(local.id, this.credentialExportFolder),
|
||||||
})) as Array<ExportableCredential & { filename: string }>;
|
})) as Array<ExportableCredential & { filename: string }>;
|
||||||
}
|
}
|
||||||
|
@ -339,14 +338,13 @@ export class SourceControlImportService {
|
||||||
(e) => e.id === credential.id && e.type === credential.type,
|
(e) => e.id === credential.id && e.type === credential.type,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { name, type, data, id, nodesAccess } = credential;
|
const { name, type, data, id } = credential;
|
||||||
const newCredentialObject = new Credentials({ id, name }, type, []);
|
const newCredentialObject = new Credentials({ id, name }, type);
|
||||||
if (existingCredential?.data) {
|
if (existingCredential?.data) {
|
||||||
newCredentialObject.data = existingCredential.data;
|
newCredentialObject.data = existingCredential.data;
|
||||||
} else {
|
} else {
|
||||||
newCredentialObject.setData(data);
|
newCredentialObject.setData(data);
|
||||||
}
|
}
|
||||||
newCredentialObject.nodesAccess = nodesAccess || existingCredential?.nodesAccess || [];
|
|
||||||
|
|
||||||
this.logger.debug(`Updating credential id ${newCredentialObject.id as string}`);
|
this.logger.debug(`Updating credential id ${newCredentialObject.id as string}`);
|
||||||
await Container.get(CredentialsRepository).upsert(newCredentialObject, ['id']);
|
await Container.get(CredentialsRepository).upsert(newCredentialObject, ['id']);
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import type { ICredentialDataDecryptedObject, ICredentialNodeAccess } from 'n8n-workflow';
|
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
|
||||||
|
|
||||||
export interface ExportableCredential {
|
export interface ExportableCredential {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
data: ICredentialDataDecryptedObject;
|
data: ICredentialDataDecryptedObject;
|
||||||
nodesAccess: ICredentialNodeAccess[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Email of the user who owns this credential at the source instance.
|
* Email of the user who owns this credential at the source instance.
|
||||||
|
|
|
@ -2,7 +2,6 @@ import type express from 'express';
|
||||||
import type {
|
import type {
|
||||||
BannerName,
|
BannerName,
|
||||||
ICredentialDataDecryptedObject,
|
ICredentialDataDecryptedObject,
|
||||||
ICredentialNodeAccess,
|
|
||||||
IDataObject,
|
IDataObject,
|
||||||
INodeCredentialTestRequest,
|
INodeCredentialTestRequest,
|
||||||
INodeCredentials,
|
INodeCredentials,
|
||||||
|
@ -158,7 +157,6 @@ export declare namespace CredentialRequest {
|
||||||
id: string; // delete if sent
|
id: string; // delete if sent
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
nodesAccess: ICredentialNodeAccess[];
|
|
||||||
data: ICredentialDataDecryptedObject;
|
data: ICredentialDataDecryptedObject;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,5 @@ test('import:credentials should import a credential', async () => {
|
||||||
expect(after.length).toBe(1);
|
expect(after.length).toBe(1);
|
||||||
expect(after[0].name).toBe('cred-aws-test');
|
expect(after[0].name).toBe('cred-aws-test');
|
||||||
expect(after[0].id).toBe('123');
|
expect(after[0].id).toBe('123');
|
||||||
expect(after[0].nodesAccess).toStrictEqual([]);
|
|
||||||
mockExit.mockRestore();
|
mockExit.mockRestore();
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
"accessKeyId": "999999999999",
|
"accessKeyId": "999999999999",
|
||||||
"secretAccessKey": "aaaaaaaaaaaaa"
|
"secretAccessKey": "aaaaaaaaaaaaa"
|
||||||
},
|
},
|
||||||
"type": "aws",
|
"type": "aws"
|
||||||
"nodesAccess": ""
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -264,11 +264,10 @@ describe('GET /credentials', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function validateCredential(credential: ListQuery.Credentials.WithOwnedByAndSharedWith) {
|
function validateCredential(credential: ListQuery.Credentials.WithOwnedByAndSharedWith) {
|
||||||
const { name, type, nodesAccess, sharedWith, ownedBy } = credential;
|
const { name, type, sharedWith, ownedBy } = credential;
|
||||||
|
|
||||||
expect(typeof name).toBe('string');
|
expect(typeof name).toBe('string');
|
||||||
expect(typeof type).toBe('string');
|
expect(typeof type).toBe('string');
|
||||||
expect(typeof nodesAccess[0].nodeType).toBe('string');
|
|
||||||
expect('data' in credential).toBe(false);
|
expect('data' in credential).toBe(false);
|
||||||
|
|
||||||
if (sharedWith) expect(Array.isArray(sharedWith)).toBe(true);
|
if (sharedWith) expect(Array.isArray(sharedWith)).toBe(true);
|
||||||
|
|
|
@ -539,7 +539,6 @@ describe('PUT /credentials/:id/share', () => {
|
||||||
function validateMainCredentialData(credential: ListQuery.Credentials.WithOwnedByAndSharedWith) {
|
function validateMainCredentialData(credential: ListQuery.Credentials.WithOwnedByAndSharedWith) {
|
||||||
expect(typeof credential.name).toBe('string');
|
expect(typeof credential.name).toBe('string');
|
||||||
expect(typeof credential.type).toBe('string');
|
expect(typeof credential.type).toBe('string');
|
||||||
expect(typeof credential.nodesAccess[0].nodeType).toBe('string');
|
|
||||||
expect(credential.ownedBy).toBeDefined();
|
expect(credential.ownedBy).toBeDefined();
|
||||||
expect(Array.isArray(credential.sharedWith)).toBe(true);
|
expect(Array.isArray(credential.sharedWith)).toBe(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,21 +96,16 @@ describe('POST /credentials', () => {
|
||||||
|
|
||||||
expect(response.statusCode).toBe(200);
|
expect(response.statusCode).toBe(200);
|
||||||
|
|
||||||
const { id, name, type, nodesAccess, data: encryptedData } = response.body.data;
|
const { id, name, type, data: encryptedData } = response.body.data;
|
||||||
|
|
||||||
expect(name).toBe(payload.name);
|
expect(name).toBe(payload.name);
|
||||||
expect(type).toBe(payload.type);
|
expect(type).toBe(payload.type);
|
||||||
if (!payload.nodesAccess) {
|
|
||||||
fail('Payload did not contain a nodesAccess array');
|
|
||||||
}
|
|
||||||
expect(nodesAccess[0].nodeType).toBe(payload.nodesAccess[0].nodeType);
|
|
||||||
expect(encryptedData).not.toBe(payload.data);
|
expect(encryptedData).not.toBe(payload.data);
|
||||||
|
|
||||||
const credential = await Container.get(CredentialsRepository).findOneByOrFail({ id });
|
const credential = await Container.get(CredentialsRepository).findOneByOrFail({ id });
|
||||||
|
|
||||||
expect(credential.name).toBe(payload.name);
|
expect(credential.name).toBe(payload.name);
|
||||||
expect(credential.type).toBe(payload.type);
|
expect(credential.type).toBe(payload.type);
|
||||||
expect(credential.nodesAccess[0].nodeType).toBe(payload.nodesAccess[0].nodeType);
|
|
||||||
expect(credential.data).not.toBe(payload.data);
|
expect(credential.data).not.toBe(payload.data);
|
||||||
|
|
||||||
const sharedCredential = await Container.get(SharedCredentialsRepository).findOneOrFail({
|
const sharedCredential = await Container.get(SharedCredentialsRepository).findOneOrFail({
|
||||||
|
@ -258,14 +253,10 @@ describe('PATCH /credentials/:id', () => {
|
||||||
|
|
||||||
expect(response.statusCode).toBe(200);
|
expect(response.statusCode).toBe(200);
|
||||||
|
|
||||||
const { id, name, type, nodesAccess, data: encryptedData } = response.body.data;
|
const { id, name, type, data: encryptedData } = response.body.data;
|
||||||
|
|
||||||
expect(name).toBe(patchPayload.name);
|
expect(name).toBe(patchPayload.name);
|
||||||
expect(type).toBe(patchPayload.type);
|
expect(type).toBe(patchPayload.type);
|
||||||
if (!patchPayload.nodesAccess) {
|
|
||||||
fail('Payload did not contain a nodesAccess array');
|
|
||||||
}
|
|
||||||
expect(nodesAccess[0].nodeType).toBe(patchPayload.nodesAccess[0].nodeType);
|
|
||||||
|
|
||||||
expect(encryptedData).not.toBe(patchPayload.data);
|
expect(encryptedData).not.toBe(patchPayload.data);
|
||||||
|
|
||||||
|
@ -273,7 +264,6 @@ describe('PATCH /credentials/:id', () => {
|
||||||
|
|
||||||
expect(credential.name).toBe(patchPayload.name);
|
expect(credential.name).toBe(patchPayload.name);
|
||||||
expect(credential.type).toBe(patchPayload.type);
|
expect(credential.type).toBe(patchPayload.type);
|
||||||
expect(credential.nodesAccess[0].nodeType).toBe(patchPayload.nodesAccess[0].nodeType);
|
|
||||||
expect(credential.data).not.toBe(patchPayload.data);
|
expect(credential.data).not.toBe(patchPayload.data);
|
||||||
|
|
||||||
const sharedCredential = await Container.get(SharedCredentialsRepository).findOneOrFail({
|
const sharedCredential = await Container.get(SharedCredentialsRepository).findOneOrFail({
|
||||||
|
@ -304,7 +294,6 @@ describe('PATCH /credentials/:id', () => {
|
||||||
const credentialObject = new Credentials(
|
const credentialObject = new Credentials(
|
||||||
{ id: credential.id, name: credential.name },
|
{ id: credential.id, name: credential.name },
|
||||||
credential.type,
|
credential.type,
|
||||||
credential.nodesAccess,
|
|
||||||
credential.data,
|
credential.data,
|
||||||
);
|
);
|
||||||
expect(credentialObject.getData()).toStrictEqual(patchPayload.data);
|
expect(credentialObject.getData()).toStrictEqual(patchPayload.data);
|
||||||
|
@ -327,23 +316,17 @@ describe('PATCH /credentials/:id', () => {
|
||||||
|
|
||||||
expect(response.statusCode).toBe(200);
|
expect(response.statusCode).toBe(200);
|
||||||
|
|
||||||
const { id, name, type, nodesAccess, data: encryptedData } = response.body.data;
|
const { id, name, type, data: encryptedData } = response.body.data;
|
||||||
|
|
||||||
expect(name).toBe(patchPayload.name);
|
expect(name).toBe(patchPayload.name);
|
||||||
expect(type).toBe(patchPayload.type);
|
expect(type).toBe(patchPayload.type);
|
||||||
|
|
||||||
if (!patchPayload.nodesAccess) {
|
|
||||||
fail('Payload did not contain a nodesAccess array');
|
|
||||||
}
|
|
||||||
expect(nodesAccess[0].nodeType).toBe(patchPayload.nodesAccess[0].nodeType);
|
|
||||||
|
|
||||||
expect(encryptedData).not.toBe(patchPayload.data);
|
expect(encryptedData).not.toBe(patchPayload.data);
|
||||||
|
|
||||||
const credential = await Container.get(CredentialsRepository).findOneByOrFail({ id });
|
const credential = await Container.get(CredentialsRepository).findOneByOrFail({ id });
|
||||||
|
|
||||||
expect(credential.name).toBe(patchPayload.name);
|
expect(credential.name).toBe(patchPayload.name);
|
||||||
expect(credential.type).toBe(patchPayload.type);
|
expect(credential.type).toBe(patchPayload.type);
|
||||||
expect(credential.nodesAccess[0].nodeType).toBe(patchPayload.nodesAccess[0].nodeType);
|
|
||||||
expect(credential.data).not.toBe(patchPayload.data);
|
expect(credential.data).not.toBe(patchPayload.data);
|
||||||
|
|
||||||
const sharedCredential = await Container.get(SharedCredentialsRepository).findOneOrFail({
|
const sharedCredential = await Container.get(SharedCredentialsRepository).findOneOrFail({
|
||||||
|
@ -545,11 +528,10 @@ describe('GET /credentials/:id', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function validateMainCredentialData(credential: ListQuery.Credentials.WithOwnedByAndSharedWith) {
|
function validateMainCredentialData(credential: ListQuery.Credentials.WithOwnedByAndSharedWith) {
|
||||||
const { name, type, nodesAccess, sharedWith, ownedBy } = credential;
|
const { name, type, sharedWith, ownedBy } = credential;
|
||||||
|
|
||||||
expect(typeof name).toBe('string');
|
expect(typeof name).toBe('string');
|
||||||
expect(typeof type).toBe('string');
|
expect(typeof type).toBe('string');
|
||||||
expect(typeof nodesAccess?.[0].nodeType).toBe('string');
|
|
||||||
|
|
||||||
if (sharedWith) {
|
if (sharedWith) {
|
||||||
expect(Array.isArray(sharedWith)).toBe(true);
|
expect(Array.isArray(sharedWith)).toBe(true);
|
||||||
|
@ -568,23 +550,15 @@ function validateMainCredentialData(credential: ListQuery.Credentials.WithOwnedB
|
||||||
const INVALID_PAYLOADS = [
|
const INVALID_PAYLOADS = [
|
||||||
{
|
{
|
||||||
type: randomName(),
|
type: randomName(),
|
||||||
nodesAccess: [{ nodeType: randomName() }],
|
|
||||||
data: { accessToken: randomString(6, 16) },
|
data: { accessToken: randomString(6, 16) },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: randomName(),
|
name: randomName(),
|
||||||
nodesAccess: [{ nodeType: randomName() }],
|
|
||||||
data: { accessToken: randomString(6, 16) },
|
data: { accessToken: randomString(6, 16) },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: randomName(),
|
name: randomName(),
|
||||||
type: randomName(),
|
type: randomName(),
|
||||||
data: { accessToken: randomString(6, 16) },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: randomName(),
|
|
||||||
type: randomName(),
|
|
||||||
nodesAccess: [{ nodeType: randomName() }],
|
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
undefined,
|
undefined,
|
||||||
|
|
|
@ -54,7 +54,6 @@ describe('SourceControlImportService', () => {
|
||||||
name: 'My Credential',
|
name: 'My Credential',
|
||||||
type: 'someCredentialType',
|
type: 'someCredentialType',
|
||||||
data: {},
|
data: {},
|
||||||
nodesAccess: [],
|
|
||||||
ownedBy: member.email, // user at source instance owns credential
|
ownedBy: member.email, // user at source instance owns credential
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,7 +89,6 @@ describe('SourceControlImportService', () => {
|
||||||
name: 'My Credential',
|
name: 'My Credential',
|
||||||
type: 'someCredentialType',
|
type: 'someCredentialType',
|
||||||
data: {},
|
data: {},
|
||||||
nodesAccess: [],
|
|
||||||
ownedBy: null,
|
ownedBy: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,7 +124,6 @@ describe('SourceControlImportService', () => {
|
||||||
name: 'My Credential',
|
name: 'My Credential',
|
||||||
type: 'someCredentialType',
|
type: 'someCredentialType',
|
||||||
data: {},
|
data: {},
|
||||||
nodesAccess: [],
|
|
||||||
ownedBy: 'user@test.com', // user at source instance owns credential
|
ownedBy: 'user@test.com', // user at source instance owns credential
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -258,7 +258,6 @@ const credentialPayload = (): CredentialPayload => ({
|
||||||
|
|
||||||
const dbCredential = () => {
|
const dbCredential = () => {
|
||||||
const credential = credentialPayload();
|
const credential = credentialPayload();
|
||||||
credential.nodesAccess = [{ nodeType: credential.type }];
|
|
||||||
|
|
||||||
return credential;
|
return credential;
|
||||||
};
|
};
|
||||||
|
@ -276,13 +275,6 @@ const INVALID_PAYLOADS = [
|
||||||
name: randomName(),
|
name: randomName(),
|
||||||
type: randomName(),
|
type: randomName(),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: randomName(),
|
|
||||||
type: 'ftp',
|
|
||||||
data: {
|
|
||||||
username: randomName(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{},
|
{},
|
||||||
[],
|
[],
|
||||||
undefined,
|
undefined,
|
||||||
|
|
|
@ -33,7 +33,6 @@ test('should report credentials not in any use', async () => {
|
||||||
name: 'My Slack Credential',
|
name: 'My Slack Credential',
|
||||||
data: 'U2FsdGVkX18WjITBG4IDqrGB1xE/uzVNjtwDAG3lP7E=',
|
data: 'U2FsdGVkX18WjITBG4IDqrGB1xE/uzVNjtwDAG3lP7E=',
|
||||||
type: 'slackApi',
|
type: 'slackApi',
|
||||||
nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const workflowDetails = {
|
const workflowDetails = {
|
||||||
|
@ -79,7 +78,6 @@ test('should report credentials not in active use', async () => {
|
||||||
name: 'My Slack Credential',
|
name: 'My Slack Credential',
|
||||||
data: 'U2FsdGVkX18WjITBG4IDqrGB1xE/uzVNjtwDAG3lP7E=',
|
data: 'U2FsdGVkX18WjITBG4IDqrGB1xE/uzVNjtwDAG3lP7E=',
|
||||||
type: 'slackApi',
|
type: 'slackApi',
|
||||||
nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const credential = await Container.get(CredentialsRepository).save(credentialDetails);
|
const credential = await Container.get(CredentialsRepository).save(credentialDetails);
|
||||||
|
@ -124,7 +122,6 @@ test('should report credential in not recently executed workflow', async () => {
|
||||||
name: 'My Slack Credential',
|
name: 'My Slack Credential',
|
||||||
data: 'U2FsdGVkX18WjITBG4IDqrGB1xE/uzVNjtwDAG3lP7E=',
|
data: 'U2FsdGVkX18WjITBG4IDqrGB1xE/uzVNjtwDAG3lP7E=',
|
||||||
type: 'slackApi',
|
type: 'slackApi',
|
||||||
nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const credential = await Container.get(CredentialsRepository).save(credentialDetails);
|
const credential = await Container.get(CredentialsRepository).save(credentialDetails);
|
||||||
|
@ -192,7 +189,6 @@ test('should not report credentials in recently executed workflow', async () =>
|
||||||
name: 'My Slack Credential',
|
name: 'My Slack Credential',
|
||||||
data: 'U2FsdGVkX18WjITBG4IDqrGB1xE/uzVNjtwDAG3lP7E=',
|
data: 'U2FsdGVkX18WjITBG4IDqrGB1xE/uzVNjtwDAG3lP7E=',
|
||||||
type: 'slackApi',
|
type: 'slackApi',
|
||||||
nodesAccess: [{ nodeType: 'n8n-nodes-base.slack', date: '2022-12-21T11:23:00.561Z' }],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const credential = await Container.get(CredentialsRepository).save(credentialDetails);
|
const credential = await Container.get(CredentialsRepository).save(credentialDetails);
|
||||||
|
|
|
@ -21,7 +21,6 @@ const emptyAttributes = {
|
||||||
name: 'test',
|
name: 'test',
|
||||||
type: 'test',
|
type: 'test',
|
||||||
data: '',
|
data: '',
|
||||||
nodesAccess: [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function createManyCredentials(
|
export async function createManyCredentials(
|
||||||
|
|
|
@ -59,7 +59,6 @@ export const randomName = () => randomString(4, 8);
|
||||||
export const randomCredentialPayload = (): CredentialPayload => ({
|
export const randomCredentialPayload = (): CredentialPayload => ({
|
||||||
name: randomName(),
|
name: randomName(),
|
||||||
type: randomName(),
|
type: randomName(),
|
||||||
nodesAccess: [{ nodeType: randomName() }],
|
|
||||||
data: { accessToken: randomString(6, 16) },
|
data: { accessToken: randomString(6, 16) },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Application } from 'express';
|
import type { Application } from 'express';
|
||||||
import type { ICredentialDataDecryptedObject, ICredentialNodeAccess } from 'n8n-workflow';
|
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
|
||||||
import type { SuperAgentTest } from 'supertest';
|
import type { SuperAgentTest } from 'supertest';
|
||||||
import type { Server } from 'http';
|
import type { Server } from 'http';
|
||||||
|
|
||||||
|
@ -52,7 +52,6 @@ export interface TestServer {
|
||||||
export type CredentialPayload = {
|
export type CredentialPayload = {
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
nodesAccess?: ICredentialNodeAccess[];
|
|
||||||
data: ICredentialDataDecryptedObject;
|
data: ICredentialDataDecryptedObject;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -244,7 +244,7 @@ describe('DELETE /users/:id', () => {
|
||||||
const savedWorkflow = await createWorkflow({ name: randomName() }, member);
|
const savedWorkflow = await createWorkflow({ name: randomName() }, member);
|
||||||
|
|
||||||
const savedCredential = await saveCredential(
|
const savedCredential = await saveCredential(
|
||||||
{ name: randomName(), type: '', data: {}, nodesAccess: [] },
|
{ name: randomName(), type: '', data: {} },
|
||||||
{ user: member, role: 'credential:owner' },
|
{ user: member, role: 'credential:owner' },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -286,7 +286,7 @@ describe('DELETE /users/:id', () => {
|
||||||
const [savedWorkflow, savedCredential] = await Promise.all([
|
const [savedWorkflow, savedCredential] = await Promise.all([
|
||||||
await createWorkflow({ name: randomName() }, member),
|
await createWorkflow({ name: randomName() }, member),
|
||||||
await saveCredential(
|
await saveCredential(
|
||||||
{ name: randomName(), type: '', data: {}, nodesAccess: [] },
|
{ name: randomName(), type: '', data: {} },
|
||||||
{
|
{
|
||||||
user: member,
|
user: member,
|
||||||
role: 'credential:owner',
|
role: 'credential:owner',
|
||||||
|
|
|
@ -39,7 +39,6 @@ describe('OAuth1CredentialController', () => {
|
||||||
const credential = mock<CredentialsEntity>({
|
const credential = mock<CredentialsEntity>({
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'Test Credential',
|
name: 'Test Credential',
|
||||||
nodesAccess: [],
|
|
||||||
type: 'oAuth1Api',
|
type: 'oAuth1Api',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,6 @@ describe('OAuth2CredentialController', () => {
|
||||||
const credential = mock<CredentialsEntity>({
|
const credential = mock<CredentialsEntity>({
|
||||||
id: '1',
|
id: '1',
|
||||||
name: 'Test Credential',
|
name: 'Test Credential',
|
||||||
nodesAccess: [],
|
|
||||||
type: 'oAuth2Api',
|
type: 'oAuth2Api',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,19 +6,6 @@ import { Cipher } from './Cipher';
|
||||||
export class Credentials extends ICredentials {
|
export class Credentials extends ICredentials {
|
||||||
private readonly cipher = Container.get(Cipher);
|
private readonly cipher = Container.get(Cipher);
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns if the given nodeType has access to data
|
|
||||||
*/
|
|
||||||
hasNodeAccess(nodeType: string): boolean {
|
|
||||||
for (const accessData of this.nodesAccess) {
|
|
||||||
if (accessData.nodeType === nodeType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets new credential object
|
* Sets new credential object
|
||||||
*/
|
*/
|
||||||
|
@ -29,14 +16,7 @@ export class Credentials extends ICredentials {
|
||||||
/**
|
/**
|
||||||
* Returns the decrypted credential object
|
* Returns the decrypted credential object
|
||||||
*/
|
*/
|
||||||
getData(nodeType?: string): ICredentialDataDecryptedObject {
|
getData(): ICredentialDataDecryptedObject {
|
||||||
if (nodeType && !this.hasNodeAccess(nodeType)) {
|
|
||||||
throw new ApplicationError('Node does not have access to credential', {
|
|
||||||
tags: { nodeType, credentialType: this.type },
|
|
||||||
extra: { credentialName: this.name },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.data === undefined) {
|
if (this.data === undefined) {
|
||||||
throw new ApplicationError('No data is set so nothing can be returned.');
|
throw new ApplicationError('No data is set so nothing can be returned.');
|
||||||
}
|
}
|
||||||
|
@ -65,7 +45,6 @@ export class Credentials extends ICredentials {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
nodesAccess: this.nodesAccess,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ describe('Credentials', () => {
|
||||||
|
|
||||||
describe('without nodeType set', () => {
|
describe('without nodeType set', () => {
|
||||||
test('should be able to set and read key data without initial data set', () => {
|
test('should be able to set and read key data without initial data set', () => {
|
||||||
const credentials = new Credentials({ id: null, name: 'testName' }, 'testType', []);
|
const credentials = new Credentials({ id: null, name: 'testName' }, 'testType');
|
||||||
|
|
||||||
const key = 'key1';
|
const key = 'key1';
|
||||||
const newData = 1234;
|
const newData = 1234;
|
||||||
|
@ -42,7 +42,6 @@ describe('Credentials', () => {
|
||||||
const credentials = new Credentials(
|
const credentials = new Credentials(
|
||||||
{ id: null, name: 'testName' },
|
{ id: null, name: 'testName' },
|
||||||
'testType',
|
'testType',
|
||||||
[],
|
|
||||||
initialDataEncoded,
|
initialDataEncoded,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -56,46 +55,4 @@ describe('Credentials', () => {
|
||||||
expect(credentials.getData().key1).toEqual(initialData);
|
expect(credentials.getData().key1).toEqual(initialData);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with nodeType set', () => {
|
|
||||||
test('should be able to set and read key data without initial data set', () => {
|
|
||||||
const nodeAccess = [
|
|
||||||
{
|
|
||||||
nodeType: 'base.noOp',
|
|
||||||
user: 'userName',
|
|
||||||
date: new Date(),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const credentials = new Credentials({ id: null, name: 'testName' }, 'testType', nodeAccess);
|
|
||||||
|
|
||||||
const key = 'key1';
|
|
||||||
const nodeType = 'base.noOp';
|
|
||||||
const newData = 1234;
|
|
||||||
|
|
||||||
setDataKey(credentials, key, newData);
|
|
||||||
|
|
||||||
// Should be able to read with nodeType which has access
|
|
||||||
expect(credentials.getData(nodeType)[key]).toEqual(newData);
|
|
||||||
|
|
||||||
// Should not be able to read with nodeType which does NOT have access
|
|
||||||
// expect(credentials.getData('base.otherNode')[key]).toThrowError(Error);
|
|
||||||
try {
|
|
||||||
credentials.getData('base.otherNode');
|
|
||||||
expect(true).toBe(false);
|
|
||||||
} catch (e) {
|
|
||||||
expect(e.message).toBe('Node does not have access to credential');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the data which will be saved in database
|
|
||||||
const dbData = credentials.getDataToSave();
|
|
||||||
expect(dbData.name).toEqual('testName');
|
|
||||||
expect(dbData.type).toEqual('testType');
|
|
||||||
expect(dbData.nodesAccess).toEqual(nodeAccess);
|
|
||||||
// Compare only the first 6 characters as the rest seems to change with each execution
|
|
||||||
expect(dbData.data!.slice(0, 6)).toEqual(
|
|
||||||
'U2FsdGVkX1+wpQWkj+YTzaPSNTFATjnlmFKIsUTZdhk='.slice(0, 6),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,9 +12,6 @@ export const credentialFactory = Factory.extend<ICredentialsResponse>({
|
||||||
name() {
|
name() {
|
||||||
return faker.company.name();
|
return faker.company.name();
|
||||||
},
|
},
|
||||||
nodesAccess() {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
type() {
|
type() {
|
||||||
return 'notionApi';
|
return 'notionApi';
|
||||||
},
|
},
|
||||||
|
|
|
@ -68,7 +68,6 @@ export default defineComponent({
|
||||||
updatedAt: '',
|
updatedAt: '',
|
||||||
type: '',
|
type: '',
|
||||||
name: '',
|
name: '',
|
||||||
nodesAccess: [],
|
|
||||||
sharedWith: [],
|
sharedWith: [],
|
||||||
ownedBy: {} as IUser,
|
ownedBy: {} as IUser,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -119,7 +119,6 @@ import type { ICredentialsResponse, IUser } from '@/Interface';
|
||||||
import type {
|
import type {
|
||||||
CredentialInformation,
|
CredentialInformation,
|
||||||
ICredentialDataDecryptedObject,
|
ICredentialDataDecryptedObject,
|
||||||
ICredentialNodeAccess,
|
|
||||||
ICredentialsDecrypted,
|
ICredentialsDecrypted,
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
INode,
|
INode,
|
||||||
|
@ -165,10 +164,6 @@ import { isValidCredentialResponse, isCredentialModalState } from '@/utils/typeG
|
||||||
import { isExpression, isTestableExpression } from '@/utils/expressions';
|
import { isExpression, isTestableExpression } from '@/utils/expressions';
|
||||||
import { useExternalHooks } from '@/composables/useExternalHooks';
|
import { useExternalHooks } from '@/composables/useExternalHooks';
|
||||||
|
|
||||||
interface NodeAccessMap {
|
|
||||||
[nodeType: string]: ICredentialNodeAccess | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'CredentialEdit',
|
name: 'CredentialEdit',
|
||||||
components: {
|
components: {
|
||||||
|
@ -212,7 +207,6 @@ export default defineComponent({
|
||||||
credentialName: '',
|
credentialName: '',
|
||||||
credentialData: {} as ICredentialDataDecryptedObject,
|
credentialData: {} as ICredentialDataDecryptedObject,
|
||||||
modalBus: createEventBus(),
|
modalBus: createEventBus(),
|
||||||
nodeAccess: {} as NodeAccessMap,
|
|
||||||
isDeleting: false,
|
isDeleting: false,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
isTesting: false,
|
isTesting: false,
|
||||||
|
@ -233,8 +227,6 @@ export default defineComponent({
|
||||||
isCredentialModalState(this.uiStore.modals[CREDENTIAL_EDIT_MODAL_KEY]) &&
|
isCredentialModalState(this.uiStore.modals[CREDENTIAL_EDIT_MODAL_KEY]) &&
|
||||||
this.uiStore.modals[CREDENTIAL_EDIT_MODAL_KEY].showAuthSelector === true;
|
this.uiStore.modals[CREDENTIAL_EDIT_MODAL_KEY].showAuthSelector === true;
|
||||||
|
|
||||||
this.setupNodeAccess();
|
|
||||||
|
|
||||||
if (this.mode === 'new' && this.credentialTypeName) {
|
if (this.mode === 'new' && this.credentialTypeName) {
|
||||||
this.credentialName = await this.credentialsStore.getNewCredentialName({
|
this.credentialName = await this.credentialsStore.getNewCredentialName({
|
||||||
credentialTypeName: this.defaultCredentialTypeName,
|
credentialTypeName: this.defaultCredentialTypeName,
|
||||||
|
@ -765,7 +757,6 @@ export default defineComponent({
|
||||||
name: this.credentialName,
|
name: this.credentialName,
|
||||||
type: this.credentialTypeName!,
|
type: this.credentialTypeName!,
|
||||||
data: credentialData,
|
data: credentialData,
|
||||||
nodesAccess: [],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.isRetesting = true;
|
this.isRetesting = true;
|
||||||
|
@ -817,7 +808,6 @@ export default defineComponent({
|
||||||
name: this.credentialName,
|
name: this.credentialName,
|
||||||
type: this.credentialTypeName!,
|
type: this.credentialTypeName!,
|
||||||
data: data as unknown as ICredentialDataDecryptedObject,
|
data: data as unknown as ICredentialDataDecryptedObject,
|
||||||
nodesAccess: [],
|
|
||||||
sharedWith,
|
sharedWith,
|
||||||
ownedBy,
|
ownedBy,
|
||||||
};
|
};
|
||||||
|
@ -1091,7 +1081,6 @@ export default defineComponent({
|
||||||
if (credentialsForType) {
|
if (credentialsForType) {
|
||||||
this.selectedCredential = credentialsForType.name;
|
this.selectedCredential = credentialsForType.name;
|
||||||
this.resetCredentialData();
|
this.resetCredentialData();
|
||||||
this.setupNodeAccess();
|
|
||||||
// Update current node auth type so credentials dropdown can be displayed properly
|
// Update current node auth type so credentials dropdown can be displayed properly
|
||||||
updateNodeAuthType(this.ndvStore.activeNode, type);
|
updateNodeAuthType(this.ndvStore.activeNode, type);
|
||||||
// Also update credential name but only if the default name is still used
|
// Also update credential name but only if the default name is still used
|
||||||
|
@ -1103,9 +1092,6 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setupNodeAccess(): void {
|
|
||||||
this.nodeAccess = {};
|
|
||||||
},
|
|
||||||
resetCredentialData(): void {
|
resetCredentialData(): void {
|
||||||
if (!this.credentialType) {
|
if (!this.credentialType) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -51,7 +51,6 @@ export const newCredential = (
|
||||||
updatedAt: faker.date.past().toISOString(),
|
updatedAt: faker.date.past().toISOString(),
|
||||||
id: faker.string.alphanumeric({ length: 16 }),
|
id: faker.string.alphanumeric({ length: 16 }),
|
||||||
name: faker.commerce.productName(),
|
name: faker.commerce.productName(),
|
||||||
nodesAccess: [],
|
|
||||||
...opts,
|
...opts,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -61,16 +60,6 @@ export const credentialsTelegram1: ICredentialsResponse = {
|
||||||
id: 'YaSKdvEcT1TSFrrr1',
|
id: 'YaSKdvEcT1TSFrrr1',
|
||||||
name: 'Telegram account',
|
name: 'Telegram account',
|
||||||
type: 'telegramApi',
|
type: 'telegramApi',
|
||||||
nodesAccess: [
|
|
||||||
{
|
|
||||||
nodeType: 'n8n-nodes-base.telegram',
|
|
||||||
date: new Date('2023-11-23T14:26:07.962Z'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
nodeType: 'n8n-nodes-base.telegramTrigger',
|
|
||||||
date: new Date('2023-11-23T14:26:07.962Z'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
ownedBy: {
|
ownedBy: {
|
||||||
id: '713ef3e7-9e65-4b0a-893c-8a653cbb2c4f',
|
id: '713ef3e7-9e65-4b0a-893c-8a653cbb2c4f',
|
||||||
email: 'user@n8n.io',
|
email: 'user@n8n.io',
|
||||||
|
@ -86,16 +75,6 @@ export const credentialsTelegram2: ICredentialsResponse = {
|
||||||
id: 'YaSKdvEcT1TSFrrr2',
|
id: 'YaSKdvEcT1TSFrrr2',
|
||||||
name: 'Telegram account',
|
name: 'Telegram account',
|
||||||
type: 'telegramApi',
|
type: 'telegramApi',
|
||||||
nodesAccess: [
|
|
||||||
{
|
|
||||||
nodeType: 'n8n-nodes-base.telegram',
|
|
||||||
date: new Date('2023-11-23T14:26:07.962Z'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
nodeType: 'n8n-nodes-base.telegramTrigger',
|
|
||||||
date: new Date('2023-11-23T14:26:07.962Z'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
ownedBy: {
|
ownedBy: {
|
||||||
id: '713ef3e7-9e65-4b0a-893c-8a653cbb2c4f',
|
id: '713ef3e7-9e65-4b0a-893c-8a653cbb2c4f',
|
||||||
email: 'user@n8n.io',
|
email: 'user@n8n.io',
|
||||||
|
|
|
@ -93,18 +93,10 @@ export abstract class ICredentials {
|
||||||
|
|
||||||
data: string | undefined;
|
data: string | undefined;
|
||||||
|
|
||||||
nodesAccess: ICredentialNodeAccess[];
|
constructor(nodeCredentials: INodeCredentialsDetails, type: string, data?: string) {
|
||||||
|
|
||||||
constructor(
|
|
||||||
nodeCredentials: INodeCredentialsDetails,
|
|
||||||
type: string,
|
|
||||||
nodesAccess: ICredentialNodeAccess[],
|
|
||||||
data?: string,
|
|
||||||
) {
|
|
||||||
this.id = nodeCredentials.id ?? undefined;
|
this.id = nodeCredentials.id ?? undefined;
|
||||||
this.name = nodeCredentials.name;
|
this.name = nodeCredentials.name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.nodesAccess = nodesAccess;
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +104,6 @@ export abstract class ICredentials {
|
||||||
|
|
||||||
abstract getDataToSave(): ICredentialsEncrypted;
|
abstract getDataToSave(): ICredentialsEncrypted;
|
||||||
|
|
||||||
abstract hasNodeAccess(nodeType: string): boolean;
|
|
||||||
|
|
||||||
abstract setData(data: ICredentialDataDecryptedObject): void;
|
abstract setData(data: ICredentialDataDecryptedObject): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,19 +114,10 @@ export interface IUser {
|
||||||
lastName: string;
|
lastName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defines which nodes are allowed to access the credentials and
|
|
||||||
// when that access got granted from which user
|
|
||||||
export interface ICredentialNodeAccess {
|
|
||||||
nodeType: string;
|
|
||||||
user?: string;
|
|
||||||
date?: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ICredentialsDecrypted {
|
export interface ICredentialsDecrypted {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
nodesAccess: ICredentialNodeAccess[];
|
|
||||||
data?: ICredentialDataDecryptedObject;
|
data?: ICredentialDataDecryptedObject;
|
||||||
ownedBy?: IUser;
|
ownedBy?: IUser;
|
||||||
sharedWith?: IUser[];
|
sharedWith?: IUser[];
|
||||||
|
@ -146,7 +127,6 @@ export interface ICredentialsEncrypted {
|
||||||
id?: string;
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
nodesAccess: ICredentialNodeAccess[];
|
|
||||||
data?: string;
|
data?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +325,6 @@ export interface ICredentialData {
|
||||||
id?: string;
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
data: string; // Contains the access data as encrypted JSON string
|
data: string; // Contains the access data as encrypted JSON string
|
||||||
nodesAccess: ICredentialNodeAccess[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The encrypted credentials which the nodes can access
|
// The encrypted credentials which the nodes can access
|
||||||
|
|
|
@ -46,10 +46,6 @@ export interface INodeTypesObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Credentials extends ICredentials {
|
export class Credentials extends ICredentials {
|
||||||
hasNodeAccess() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
setData(data: ICredentialDataDecryptedObject) {
|
setData(data: ICredentialDataDecryptedObject) {
|
||||||
this.data = JSON.stringify(data);
|
this.data = JSON.stringify(data);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +67,6 @@ export class Credentials extends ICredentials {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
type: this.type,
|
type: this.type,
|
||||||
data: this.data,
|
data: this.data,
|
||||||
nodesAccess: this.nodesAccess,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue