mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
* ✨ Inicial setup * ⚡ Add authentication handler * ⚡ Add GET /users route * ⚡ Improvements * 👕 Fix linting issues * ⚡ Add GET /users/:identifier endpoint * ⚡ Add POST /users endpoint * ⚡ Add DELETE /users/:identifier endpoint * ⚡ Return error using express native functions * 👕 Fix linting issue * ⚡ Possibility to add custom middleware * ⚡ Refactor POST /users * ⚡ Refactor DELETE /users * ⚡ Improve cleaning function * ⚡ Refactor GET /users and /users/:identifier * ⚡ Add API spec to route * ⚡ Add raw option to response helper * 🐛 Fix issue adding custom middleware * ⚡ Enable includeRole parameter in GET /users/:identifier * ⚡ Fix linting issues after merge * ⚡ Add missing config variable * ⚡ General improvements ⚡ asasas * ⚡ Add POST /users tests * Debug public API tests * Fix both sets of tests * ⚡ Improvements * ⚡ Load api versions dynamically * ⚡ Add endpoints to UM to create/delete an API Key * ⚡ Add index to apiKey column * 👕 Fix linting issue * ⚡ Clean open api spec * ⚡ Improvements * ⚡ Skip tests * 🐛 Fix bug with test * ⚡ Fix issue with the open api spec * ⚡ Fix merge issue * ⚡ Move token enpoints from /users to /me * ⚡ Apply feedback to openapi.yml * ⚡ Improvements to api-key endpoints * 🐛 Fix test to suport API dynamic loading * ⚡ Expose swagger ui in GET /{version}/docs * ⚡ Allow to disable public api via env variable * ⚡ Change handlers structure * 🚧 WIP create credential, delete credential complete * 🐛 fix route for creating api key * ⚡ return api key of authenticated user * ⚡ Expose public api activation to the settings * ⬆️ Update package-lock.json file * ⚡ Add execution resource * ⚡ Fix linting issues * 🛠 conditional public api endpoints excluding * ⚡️ create credential complete * ✨ Added n8n-card component. Added spacing utility classes. * ♻️ Made use of n8n-card in existing components. * ✨ Added api key setup view. * ✨ Added api keys get/create/delete actions. * ✨ Added public api permissions handling. * ♻️ Temporarily disabling card tests. * ♻️ Changed translations. Storing api key only in component. * ✨ Added utilities storybook entry * ♻️ Changed default value for generic copy input. * 🧹 clean up createCredential * ⚡ Add workflow resource to openapi spec * 🐛 Fix naming with env variable * ⚡ Allow multifile openapi spec * ⚡ Add POST /workflows/:workflowId/activate * fix up view, fix issues * remove delete api key modal * remove unused prop * clean up store api * remove getter * remove unused dispatch * fix component size to match * use existing components * match figma closely * fix bug when um is disabled in sidebar * set copy input color * remove unused import * ⚡ Remove css path * ⚡ Add POST /workflows/:workflowId/desactivate * ⚡ Add POST /workflows * Revert "⚡ Remove css path" a3d0a71719834ef37c88c23c83dfe662e96185aa * attempt to fix docker image issue * revert dockerfile test * disable public api * disable api differently * Revert "disable api differently" b70e29433e45934975e41ffdc32e288988aba9b0 * Revert "disable public api" 886e5164fb4135c164f77561bdb4427e5cd44ac1 * remove unused box * ⚡ PUT /workflows/:workflowId * ⚡ Refactor workflow endpoints * ⚡ Refactor executions endpoints * ⚡ Fix typo * ✅ add credentials tests * ✅ adjust users tests * update text * add try it out link * ⚡ Add delete, getAll and get to the workflow resource * address spacing comments * ⚡️ apply correct structure * ⚡ Add missing test to user resource and fix some issues * ⚡ Add workflow tests * ⚡ Add missing workflow tests and fix some issues * ⚡ Executions tests * ⚡ finish execution tests * ⚡ Validate credentials data depending on type * ⚡️ implement review comments * 👕 fix lint issues * ⚡ Add apiKey to sanatizeUser * ⚡ Fix issues with spec and tests * ⚡ Add new structure * ⚡ Validate credentials type and properties * ⚡ Make all endpoints except /users independent on UM * ⚡ Add instance base path to swagger UI * ⚡ Remove testing endpoints * ⚡ Fix issue with openapi tags * ⚡ Add endpoint GET /credentialTypes/:id/schema * 🐛 Fix issue adding json middleware to public api * ⚡ Add API playground path to FE * ⚡ Add telemetry and external hooks * 🐛 Fix issue with user tests * ⚡ Move /credentialTypes under /credentials * ⚡ Add test to GET /credentials/schema/:id * 🛠 refactor schema naming * ⚡ Add DB migrations asas * ✅ add tests for crd apiKey * ✨ Added API View telemetry events. * ⚡ Remove rsync from the building process as it is missing on alpine base image * ⚡ add missing BE telemetry events * 🐛 Fix credential tests * ⚡ address outstanding feedback * 🔨 Remove move:openapi script * ⬆️ update dependency * ⬆️ update package-lock.json * 👕 Fix linting issue * 🐛 Fix package.json issue * 🐛 fix migrations and tests * 🐛 fix typos + naming * 🚧 WIP fixing tests * ⚡ Add json schema validation * ⚡ Add missing fields to node schema * ⚡ Add limit max upper limit * ⚡ Rename id paths * 🐛 Fix tests * Add package-lock.jsonto custom dockerfile * ⬆️ Update package-lock.json * 🐛 Fix issue with build * ✏️ add beta label to api view * 🔥 Remove user endpoints * ⚡ Add schema examples to GET /credentials/schema/:id * 🔥 Remove user endpoints tests * 🐛 Fix tests * 🎨 adapt points from design review * 🔥 remove unnecessary text-align * ⚡️ update UI * 🐛 Fix issue with executions filter * ⚡ Add tags filter to GET /workflows * ⚡ Add missing error messages * ✅ add and update public api tests * ✅ add tests for owner activiating/deactivating non-owned wfs * 🧪 add tests for filter for tags * 🧪 add tests for more filter params * 🐛 fix inclusion of tags * 🛠 enhance readability * ⚡️ small refactorings * 💄 improving readability/naming * ⚡ Set API latest version dinamically * Add comments to toJsonSchema function * ⚡ Fix issue * ⚡ Make execution data usable * ⚡ Fix validation issue * ⚡ Rename data field and change parameter and options * 🐛 Fix issue parameter "detailsFieldFormat" not resolving correctly * Skip executions tests * skip workflow failing test * Rename details property to data * ⚡ Add includeData parameter * 🐛 Fix issue with openapi spec * 🐛 Fix linting issue * ⚡ Fix execution schema Co-authored-by: Iván Ovejero <ivov.src@gmail.com> Co-authored-by: Ben Hesseldieck <b.hesseldieck@gmail.com> Co-authored-by: Alex Grozav <alex@grozav.com> Co-authored-by: Mutasem <mutdmour@gmail.com> Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
250 lines
7.6 KiB
TypeScript
250 lines
7.6 KiB
TypeScript
/* eslint-disable no-restricted-syntax */
|
|
/* eslint-disable no-underscore-dangle */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
import { FindOneOptions } from 'typeorm';
|
|
import { UserSettings, Credentials } from 'n8n-core';
|
|
import { IDataObject, INodeProperties, INodePropertyOptions } from 'n8n-workflow';
|
|
import { Db, ICredentialsDb } from '../../../..';
|
|
import { CredentialsEntity } from '../../../../databases/entities/CredentialsEntity';
|
|
import { SharedCredentials } from '../../../../databases/entities/SharedCredentials';
|
|
import { User } from '../../../../databases/entities/User';
|
|
import { externalHooks } from '../../../../Server';
|
|
import { IDependency, IJsonSchema } from '../../../types';
|
|
|
|
export async function getCredentials(
|
|
credentialId: number | string,
|
|
): Promise<ICredentialsDb | undefined> {
|
|
return Db.collections.Credentials.findOne(credentialId);
|
|
}
|
|
|
|
export async function getSharedCredentials(
|
|
userId: string,
|
|
credentialId: number | string,
|
|
relations?: string[],
|
|
): Promise<SharedCredentials | undefined> {
|
|
const options: FindOneOptions = {
|
|
where: {
|
|
user: { id: userId },
|
|
credentials: { id: credentialId },
|
|
},
|
|
};
|
|
|
|
if (relations) {
|
|
options.relations = relations;
|
|
}
|
|
|
|
return Db.collections.SharedCredentials.findOne(options);
|
|
}
|
|
|
|
export async function createCredential(
|
|
properties: Partial<CredentialsEntity>,
|
|
): Promise<CredentialsEntity> {
|
|
const newCredential = new CredentialsEntity();
|
|
|
|
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) => {
|
|
// eslint-disable-next-line no-param-reassign
|
|
nodeAccess.date = new Date();
|
|
});
|
|
}
|
|
|
|
return newCredential;
|
|
}
|
|
|
|
export async function saveCredential(
|
|
credential: CredentialsEntity,
|
|
user: User,
|
|
encryptedData: ICredentialsDb,
|
|
): Promise<CredentialsEntity> {
|
|
const role = await Db.collections.Role.findOneOrFail({
|
|
name: 'owner',
|
|
scope: 'credential',
|
|
});
|
|
|
|
await externalHooks.run('credentials.create', [encryptedData]);
|
|
|
|
return Db.transaction(async (transactionManager) => {
|
|
const savedCredential = await transactionManager.save<CredentialsEntity>(credential);
|
|
|
|
savedCredential.data = credential.data;
|
|
|
|
const newSharedCredential = new SharedCredentials();
|
|
|
|
Object.assign(newSharedCredential, {
|
|
role,
|
|
user,
|
|
credentials: savedCredential,
|
|
});
|
|
|
|
await transactionManager.save<SharedCredentials>(newSharedCredential);
|
|
|
|
return savedCredential;
|
|
});
|
|
}
|
|
|
|
export async function removeCredential(credentials: CredentialsEntity): Promise<ICredentialsDb> {
|
|
await externalHooks.run('credentials.delete', [credentials.id]);
|
|
return Db.collections.Credentials.remove(credentials);
|
|
}
|
|
|
|
export async function encryptCredential(credential: CredentialsEntity): Promise<ICredentialsDb> {
|
|
const encryptionKey = await UserSettings.getEncryptionKey();
|
|
|
|
// Encrypt the data
|
|
const coreCredential = new Credentials(
|
|
{ id: null, name: credential.name },
|
|
credential.type,
|
|
credential.nodesAccess,
|
|
);
|
|
|
|
// @ts-ignore
|
|
coreCredential.setData(credential.data, encryptionKey);
|
|
|
|
return coreCredential.getDataToSave() as ICredentialsDb;
|
|
}
|
|
|
|
export function sanitizeCredentials(credentials: CredentialsEntity): Partial<CredentialsEntity>;
|
|
export function sanitizeCredentials(
|
|
credentials: CredentialsEntity[],
|
|
): Array<Partial<CredentialsEntity>>;
|
|
|
|
export function sanitizeCredentials(
|
|
credentials: CredentialsEntity | CredentialsEntity[],
|
|
): Partial<CredentialsEntity> | Array<Partial<CredentialsEntity>> {
|
|
const argIsArray = Array.isArray(credentials);
|
|
const credentialsList = argIsArray ? credentials : [credentials];
|
|
|
|
const sanitizedCredentials = credentialsList.map((credential) => {
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
const { data, nodesAccess, shared, ...rest } = credential;
|
|
return rest;
|
|
});
|
|
|
|
return argIsArray ? sanitizedCredentials : sanitizedCredentials[0];
|
|
}
|
|
|
|
/**
|
|
* toJsonSchema
|
|
* Take an array of crendentials parameter and map it
|
|
* to a JSON Schema (see https://json-schema.org/). With
|
|
* the JSON Schema defintion we can validate the credential's shape
|
|
* @param properties - Credentials properties
|
|
* @returns The credentials schema definition.
|
|
*/
|
|
export function toJsonSchema(properties: INodeProperties[]): IDataObject {
|
|
const jsonSchema: IJsonSchema = {
|
|
additionalProperties: false,
|
|
type: 'object',
|
|
properties: {},
|
|
allOf: [],
|
|
required: [],
|
|
};
|
|
|
|
const optionsValues: { [key: string]: string[] } = {};
|
|
const resolveProperties: string[] = [];
|
|
|
|
// get all posible values of properties type "options"
|
|
// so we can later resolve the displayOptions dependencies
|
|
properties
|
|
.filter((property) => property.type === 'options')
|
|
.forEach((property) => {
|
|
Object.assign(optionsValues, {
|
|
[property.name]: property.options?.map((option: INodePropertyOptions) => option.value),
|
|
});
|
|
});
|
|
|
|
let requiredFields: string[] = [];
|
|
|
|
const propertyRequiredDependencies: { [key: string]: IDependency } = {};
|
|
|
|
// add all credential's properties to the properties
|
|
// object in the JSON Schema definition. This allows us
|
|
// to later validate that only this properties are set in
|
|
// the credentials sent in the API call.
|
|
properties.forEach((property) => {
|
|
requiredFields.push(property.name);
|
|
if (property.type === 'options') {
|
|
// if the property is type options,
|
|
// include all possible values in the anum property.
|
|
Object.assign(jsonSchema.properties, {
|
|
[property.name]: {
|
|
type: 'string',
|
|
enum: property.options?.map((data: INodePropertyOptions) => data.value),
|
|
},
|
|
});
|
|
} else {
|
|
Object.assign(jsonSchema.properties, {
|
|
[property.name]: {
|
|
type: property.type,
|
|
},
|
|
});
|
|
}
|
|
|
|
// if the credential property has a dependency
|
|
// then add a JSON Schema condition that satisfy each property value
|
|
// e.x: If A has value X then required B, else required C
|
|
// see https://json-schema.org/understanding-json-schema/reference/conditionals.html#if-then-else
|
|
if (property.displayOptions?.show) {
|
|
const dependantName = Object.keys(property.displayOptions?.show)[0] || '';
|
|
const displayOptionsValues = property.displayOptions.show[dependantName];
|
|
let dependantValue: string | number | boolean = '';
|
|
|
|
if (displayOptionsValues && Array.isArray(displayOptionsValues) && displayOptionsValues[0]) {
|
|
// eslint-disable-next-line prefer-destructuring
|
|
dependantValue = displayOptionsValues[0];
|
|
}
|
|
|
|
if (propertyRequiredDependencies[dependantName] === undefined) {
|
|
propertyRequiredDependencies[dependantName] = {};
|
|
}
|
|
|
|
if (!resolveProperties.includes(dependantName)) {
|
|
propertyRequiredDependencies[dependantName] = {
|
|
if: {
|
|
properties: {
|
|
[dependantName]: {
|
|
enum: [dependantValue],
|
|
},
|
|
},
|
|
},
|
|
then: {
|
|
oneOf: [],
|
|
},
|
|
else: {
|
|
allOf: [],
|
|
},
|
|
};
|
|
}
|
|
|
|
propertyRequiredDependencies[dependantName].then?.oneOf.push({ required: [property.name] });
|
|
propertyRequiredDependencies[dependantName].else?.allOf.push({
|
|
not: { required: [property.name] },
|
|
});
|
|
|
|
resolveProperties.push(dependantName);
|
|
// remove global required
|
|
requiredFields = requiredFields.filter((field) => field !== property.name);
|
|
}
|
|
});
|
|
Object.assign(jsonSchema, { required: requiredFields });
|
|
|
|
jsonSchema.allOf = Object.values(propertyRequiredDependencies);
|
|
|
|
if (!jsonSchema.allOf.length) {
|
|
delete jsonSchema.allOf;
|
|
}
|
|
|
|
return jsonSchema as unknown as IDataObject;
|
|
}
|