diff --git a/cypress/e2e/1-workflows.cy.ts b/cypress/e2e/1-workflows.cy.ts index bfa5922c2a..0dc68cbae9 100644 --- a/cypress/e2e/1-workflows.cy.ts +++ b/cypress/e2e/1-workflows.cy.ts @@ -1,18 +1,26 @@ import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { v4 as uuid } from 'uuid'; +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; const WorkflowsPage = new WorkflowsPageClass(); const WorkflowPage = new WorkflowPageClass(); const multipleWorkflowsCount = 5; +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Workflows', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); cy.visit(WorkflowsPage.url); }); diff --git a/cypress/e2e/10-undo-redo.cy.ts b/cypress/e2e/10-undo-redo.cy.ts index 90e9e558eb..b8c4113fd7 100644 --- a/cypress/e2e/10-undo-redo.cy.ts +++ b/cypress/e2e/10-undo-redo.cy.ts @@ -1,7 +1,8 @@ -import { CODE_NODE_NAME, SET_NODE_NAME } from './../constants'; +import { CODE_NODE_NAME, DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD, SET_NODE_NAME } from './../constants'; import { SCHEDULE_TRIGGER_NODE_NAME } from '../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { NDV } from '../pages/ndv'; +import { randFirstName, randLastName } from '@ngneat/falso'; // Suite-specific constants const CODE_NODE_NEW_NAME = 'Something else'; @@ -9,12 +10,18 @@ const CODE_NODE_NEW_NAME = 'Something else'; const WorkflowPage = new WorkflowPageClass(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Undo/Redo', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); WorkflowPage.actions.visit(); }); diff --git a/cypress/e2e/11-inline-expression-editor.cy.ts b/cypress/e2e/11-inline-expression-editor.cy.ts index 4d9a46bb31..929c0721df 100644 --- a/cypress/e2e/11-inline-expression-editor.cy.ts +++ b/cypress/e2e/11-inline-expression-editor.cy.ts @@ -1,13 +1,21 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + const WorkflowPage = new WorkflowPageClass(); describe('Inline expression editor', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); WorkflowPage.actions.visit(); WorkflowPage.actions.addInitialNodeToCanvas('Manual'); WorkflowPage.actions.addNodeToCanvas('Hacker News'); diff --git a/cypress/e2e/12-canvas-actions.cy.ts b/cypress/e2e/12-canvas-actions.cy.ts index 40bd9d168e..b485868f05 100644 --- a/cypress/e2e/12-canvas-actions.cy.ts +++ b/cypress/e2e/12-canvas-actions.cy.ts @@ -6,16 +6,25 @@ import { SET_NODE_NAME, IF_NODE_NAME, HTTP_REQUEST_NODE_NAME, + DEFAULT_USER_EMAIL, + DEFAULT_USER_PASSWORD, } from './../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; +import { randFirstName, randLastName } from '@ngneat/falso'; + +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); const WorkflowPage = new WorkflowPageClass(); describe('Canvas Actions', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); WorkflowPage.actions.visit(); }); diff --git a/cypress/e2e/12-canvas.cy.ts b/cypress/e2e/12-canvas.cy.ts index 65428acda8..f7a69e7a55 100644 --- a/cypress/e2e/12-canvas.cy.ts +++ b/cypress/e2e/12-canvas.cy.ts @@ -8,8 +8,11 @@ import { IF_NODE_NAME, MERGE_NODE_NAME, HTTP_REQUEST_NODE_NAME, + DEFAULT_USER_EMAIL, + DEFAULT_USER_PASSWORD, } from './../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; +import { randFirstName, randLastName } from '@ngneat/falso'; const WorkflowPage = new WorkflowPageClass(); @@ -20,12 +23,18 @@ const ZOOM_OUT_X1_FACTOR = 0.8; const ZOOM_OUT_X2_FACTOR = 0.64; const RENAME_NODE_NAME = 'Something else'; +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Canvas Node Manipulation and Navigation', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); WorkflowPage.actions.visit(); }); diff --git a/cypress/e2e/13-pinning.cy.ts b/cypress/e2e/13-pinning.cy.ts index 004e728845..d94cf3a5fa 100644 --- a/cypress/e2e/13-pinning.cy.ts +++ b/cypress/e2e/13-pinning.cy.ts @@ -1,4 +1,7 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; import { + DEFAULT_USER_EMAIL, + DEFAULT_USER_PASSWORD, HTTP_REQUEST_NODE_NAME, MANUAL_TRIGGER_NODE_NAME, PIPEDRIVE_NODE_NAME, @@ -9,12 +12,18 @@ import { WorkflowPage, NDV } from '../pages'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Data pinning', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); }); diff --git a/cypress/e2e/14-data-transformation-expressions.cy.ts b/cypress/e2e/14-data-transformation-expressions.cy.ts index 43dbded37a..cbc69e5533 100644 --- a/cypress/e2e/14-data-transformation-expressions.cy.ts +++ b/cypress/e2e/14-data-transformation-expressions.cy.ts @@ -1,14 +1,22 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage, NDV } from '../pages'; const wf = new WorkflowPage(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Data transformation expressions', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); wf.actions.visit(); cy.window().then( diff --git a/cypress/e2e/14-mapping.cy.ts b/cypress/e2e/14-mapping.cy.ts index c7035396d3..1610500448 100644 --- a/cypress/e2e/14-mapping.cy.ts +++ b/cypress/e2e/14-mapping.cy.ts @@ -2,18 +2,27 @@ import { MANUAL_TRIGGER_NODE_NAME, MANUAL_TRIGGER_NODE_DISPLAY_NAME, SCHEDULE_TRIGGER_NODE_NAME, + DEFAULT_USER_EMAIL, + DEFAULT_USER_PASSWORD, } from './../constants'; import { WorkflowPage, NDV } from '../pages'; +import { randFirstName, randLastName } from '@ngneat/falso'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Data mapping', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); cy.window().then((win) => { diff --git a/cypress/e2e/15-scheduler-node.cy.ts b/cypress/e2e/15-scheduler-node.cy.ts index cc4b3d7758..8ad0b8ad95 100644 --- a/cypress/e2e/15-scheduler-node.cy.ts +++ b/cypress/e2e/15-scheduler-node.cy.ts @@ -1,3 +1,5 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage, WorkflowsPage, NDV } from '../pages'; import { BACKEND_BASE_URL } from '../constants'; @@ -5,12 +7,18 @@ const workflowsPage = new WorkflowsPage(); const workflowPage = new WorkflowPage(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Schedule Trigger node', async () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); }); diff --git a/cypress/e2e/16-webhook-node.cy.ts b/cypress/e2e/16-webhook-node.cy.ts index 178350a32b..1c17119b31 100644 --- a/cypress/e2e/16-webhook-node.cy.ts +++ b/cypress/e2e/16-webhook-node.cy.ts @@ -2,6 +2,13 @@ import { WorkflowPage, NDV, CredentialsModal } from '../pages'; import { v4 as uuid } from 'uuid'; import { cowBase64 } from '../support/binaryTestFiles'; import { BACKEND_BASE_URL } from '../constants'; +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; + +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); const workflowPage = new WorkflowPage(); const ndv = new NDV(); @@ -93,10 +100,11 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => { describe('Webhook Trigger node', async () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); cy.window().then((win) => { diff --git a/cypress/e2e/17-workflow-tags.cy.ts b/cypress/e2e/17-workflow-tags.cy.ts index d18c48cf7b..8b58657747 100644 --- a/cypress/e2e/17-workflow-tags.cy.ts +++ b/cypress/e2e/17-workflow-tags.cy.ts @@ -1,15 +1,23 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage } from '../pages'; const wf = new WorkflowPage(); const TEST_TAGS = ['Tag 1', 'Tag 2', 'Tag 3', 'Tag 4', 'Tag 5']; +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Workflow tags', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); wf.actions.visit(); }); diff --git a/cypress/e2e/19-execution.cy.ts b/cypress/e2e/19-execution.cy.ts index 983e5e4bba..f91c2eb6cd 100644 --- a/cypress/e2e/19-execution.cy.ts +++ b/cypress/e2e/19-execution.cy.ts @@ -1,16 +1,24 @@ import { v4 as uuid } from 'uuid'; import { NDV, WorkflowPage as WorkflowPageClass, WorkflowsPage } from '../pages'; +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; const workflowsPage = new WorkflowsPage(); const workflowPage = new WorkflowPageClass(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Execution', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); }); diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts index d8f3fc5e0e..850ba06eb2 100644 --- a/cypress/e2e/2-credentials.cy.ts +++ b/cypress/e2e/2-credentials.cy.ts @@ -33,10 +33,11 @@ const NEW_CREDENTIAL_NAME = 'Something else'; describe('Credentials', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); cy.visit(credentialsPage.url); }); diff --git a/cypress/e2e/20-workflow-executions.cy.ts b/cypress/e2e/20-workflow-executions.cy.ts index fe40633c38..4b3ae7e1a7 100644 --- a/cypress/e2e/20-workflow-executions.cy.ts +++ b/cypress/e2e/20-workflow-executions.cy.ts @@ -1,16 +1,24 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage } from '../pages'; import { WorkflowExecutionsTab } from '../pages/workflow-executions-tab'; const workflowPage = new WorkflowPage(); const executionsTab = new WorkflowExecutionsTab(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + // Test suite for executions tab describe('Current Workflow Executions', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); cy.createFixtureWorkflow('Test_workflow_4_executions_view.json', `My test workflow`); createMockExecutions(); diff --git a/cypress/e2e/21-community-nodes.cy.ts b/cypress/e2e/21-community-nodes.cy.ts index d48d365c4f..bc976330ce 100644 --- a/cypress/e2e/21-community-nodes.cy.ts +++ b/cypress/e2e/21-community-nodes.cy.ts @@ -4,17 +4,24 @@ import { CredentialsModal, WorkflowPage } from '../pages'; import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json'; import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json'; import CustomCredential from '../fixtures/Custom_credential.json'; +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; const credentialsModal = new CredentialsModal(); const nodeCreatorFeature = new NodeCreator(); const workflowPage = new WorkflowPage(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + // We separate-out the custom nodes because they require injecting nodes and credentials // so the /nodes and /credentials endpoints are intercepted and non-cached. // We want to keep the other tests as fast as possible so we don't want to break the cache in those. describe('Community Nodes', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }) beforeEach(() => { cy.intercept('/types/nodes.json', { middleware: true }, (req) => { @@ -36,6 +43,7 @@ describe('Community Nodes', () => { credentials.push(CustomCredential); }) }) + cy.signin({ email, password }); workflowPage.actions.visit(); }); diff --git a/cypress/e2e/24-ndv-paired-item.cy.ts b/cypress/e2e/24-ndv-paired-item.cy.ts index b7f6c0f437..c4d6162bfa 100644 --- a/cypress/e2e/24-ndv-paired-item.cy.ts +++ b/cypress/e2e/24-ndv-paired-item.cy.ts @@ -1,15 +1,23 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage, NDV } from '../pages'; import { v4 as uuid } from 'uuid'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('NDV', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); workflowPage.actions.renameWorkflow(uuid()); workflowPage.actions.saveWorkflowOnButtonClick(); diff --git a/cypress/e2e/25-stickies.cy.ts b/cypress/e2e/25-stickies.cy.ts index 13396efe25..e755d8e134 100644 --- a/cypress/e2e/25-stickies.cy.ts +++ b/cypress/e2e/25-stickies.cy.ts @@ -1,7 +1,14 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; const workflowPage = new WorkflowPageClass(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + function checkStickiesStyle( top: number, left: number, height: number, width: number, zIndex?: number) { workflowPage.getters.stickies().should(($el) => { expect($el).to.have.css('top', `${top}px`); @@ -16,10 +23,11 @@ function checkStickiesStyle( top: number, left: number, height: number, width: n describe('Canvas Actions', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); cy.window().then( diff --git a/cypress/e2e/26-resource-locator.cy.ts b/cypress/e2e/26-resource-locator.cy.ts index 3a00dded78..5b6cb4d3c7 100644 --- a/cypress/e2e/26-resource-locator.cy.ts +++ b/cypress/e2e/26-resource-locator.cy.ts @@ -1,3 +1,5 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage, NDV, CredentialsModal } from '../pages'; const workflowPage = new WorkflowPage(); @@ -7,12 +9,18 @@ const credentialsModal = new CredentialsModal(); const NO_CREDENTIALS_MESSAGE = 'Please add your credential'; const INVALID_CREDENTIALS_MESSAGE = 'Please check your credential'; +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Resource Locator', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); }); diff --git a/cypress/e2e/3-default-owner.cy.ts b/cypress/e2e/3-default-owner.cy.ts deleted file mode 100644 index bd9b29f036..0000000000 --- a/cypress/e2e/3-default-owner.cy.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { randFirstName, randLastName } from '@ngneat/falso'; -import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; -import { - SettingsUsersPage, - SignupPage, - WorkflowsPage, - WorkflowPage, - CredentialsPage, - CredentialsModal, - MessageBox, -} from '../pages'; -import { SettingsUsagePage } from '../pages/settings-usage'; - -import { MainSidebar, SettingsSidebar } from '../pages/sidebar'; - -const mainSidebar = new MainSidebar(); -const settingsSidebar = new SettingsSidebar(); - -const workflowsPage = new WorkflowsPage(); -const signupPage = new SignupPage(); -const workflowPage = new WorkflowPage(); - -const credentialsPage = new CredentialsPage(); -const credentialsModal = new CredentialsModal(); - -const settingsUsersPage = new SettingsUsersPage(); -const settingsUsagePage = new SettingsUsagePage(); - -const messageBox = new MessageBox(); - -const email = DEFAULT_USER_EMAIL; -const password = DEFAULT_USER_PASSWORD; -const firstName = randFirstName(); -const lastName = randLastName(); - -describe('Default owner', () => { - it('should be able to create workflows', () => { - cy.skipSetup(); - cy.createFixtureWorkflow('Test_workflow_1.json', `Test workflow`); - - // reload page, ensure owner still has access - cy.reload(); - cy.waitForLoad(); - workflowPage.getters.workflowNameInput().should('contain.value', 'Test workflow'); - }); - - it('should be able to add new credentials', () => { - cy.visit(credentialsPage.url); - - credentialsPage.getters.emptyListCreateCredentialButton().click(); - - credentialsModal.getters.newCredentialModal().should('be.visible'); - credentialsModal.getters.newCredentialTypeSelect().should('be.visible'); - credentialsModal.getters.newCredentialTypeOption('Notion API').click(); - - credentialsModal.getters.newCredentialTypeButton().click(); - - credentialsModal.getters.connectionParameter('API Key').type('1234567890'); - - credentialsModal.actions.setName('My awesome Notion account'); - credentialsModal.actions.save(); - - credentialsModal.actions.close(); - - credentialsModal.getters.newCredentialModal().should('not.exist'); - credentialsModal.getters.editCredentialModal().should('not.exist'); - - credentialsPage.getters.credentialCards().should('have.length', 1); - }); - - it('should be able to setup UM from settings', () => { - cy.visit('/'); - mainSidebar.getters.settings().should('be.visible'); - mainSidebar.actions.goToSettings(); - cy.url().should('include', settingsUsagePage.url); - - settingsSidebar.actions.goToUsers(); - cy.url().should('include', settingsUsersPage.url); - - settingsUsersPage.actions.goToOwnerSetup(); - - cy.url().should('include', signupPage.url); - }); - - it('should be able to setup instance and migrate workflows and credentials', () => { - cy.setup({ email, firstName, lastName, password }, true); - - messageBox.getters.content().should('contain.text', '1 existing workflow and 1 credential'); - - messageBox.actions.confirm(); - cy.wait('@setupRequest'); - cy.url().should('include', settingsUsersPage.url); - settingsSidebar.actions.back(); - - cy.url().should('include', workflowsPage.url); - - workflowsPage.getters.workflowCards().should('have.length', 1); - }); - - it('can click back to main menu and have migrated credential after setup', () => { - cy.signin({ email, password }); - cy.visit(workflowsPage.url); - - mainSidebar.actions.goToCredentials(); - - cy.url().should('include', credentialsPage.url); - - credentialsPage.getters.credentialCards().should('have.length', 1); - }); -}); diff --git a/cypress/e2e/4-node-creator.cy.ts b/cypress/e2e/4-node-creator.cy.ts index 187345e940..bc74d21ed0 100644 --- a/cypress/e2e/4-node-creator.cy.ts +++ b/cypress/e2e/4-node-creator.cy.ts @@ -1,17 +1,25 @@ import { NodeCreator } from '../pages/features/node-creator'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { NDV } from '../pages/ndv'; +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; const nodeCreatorFeature = new NodeCreator(); const WorkflowPage = new WorkflowPageClass(); const NDVModal = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Node Creator', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); WorkflowPage.actions.visit(); }); diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index 93198a2391..f63a7a089e 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -1,15 +1,23 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage, NDV } from '../pages'; import { v4 as uuid } from 'uuid'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('NDV', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); workflowPage.actions.visit(); workflowPage.actions.renameWorkflow(uuid()); workflowPage.actions.saveWorkflowOnButtonClick(); diff --git a/cypress/e2e/6-code-node.cy.ts b/cypress/e2e/6-code-node.cy.ts index 9a12dfccf4..c9c7cdf331 100644 --- a/cypress/e2e/6-code-node.cy.ts +++ b/cypress/e2e/6-code-node.cy.ts @@ -1,12 +1,24 @@ import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { NDV } from '../pages/ndv'; +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; const WorkflowPage = new WorkflowPageClass(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Code node', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); + }); + + beforeEach(() => { + cy.signin({ email, password }); + WorkflowPage.actions.visit(); }); it('should execute the placeholder in all-items mode successfully', () => { @@ -20,7 +32,6 @@ describe('Code node', () => { }); it('should execute the placeholder in each-item mode successfully', () => { - WorkflowPage.actions.visit(); WorkflowPage.actions.addInitialNodeToCanvas('Manual'); WorkflowPage.actions.addNodeToCanvas('Code'); WorkflowPage.actions.openNode('Code'); diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index b7f948744c..007f6fab3a 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -1,5 +1,8 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; import { CODE_NODE_NAME, + DEFAULT_USER_EMAIL, + DEFAULT_USER_PASSWORD, MANUAL_TRIGGER_NODE_NAME, META_KEY, SCHEDULE_TRIGGER_NODE_NAME, @@ -11,14 +14,20 @@ const IMPORT_WORKFLOW_URL = 'https://gist.githubusercontent.com/OlegIvaniv/010bd const DUPLICATE_WORKFLOW_NAME = 'Duplicated workflow'; const DUPLICATE_WORKFLOW_TAG = 'Duplicate'; +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + const WorkflowPage = new WorkflowPageClass(); describe('Workflow Actions', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); WorkflowPage.actions.visit(); }); diff --git a/cypress/e2e/8-http-request-node.cy.ts b/cypress/e2e/8-http-request-node.cy.ts index a40d37cf23..a905c7f087 100644 --- a/cypress/e2e/8-http-request-node.cy.ts +++ b/cypress/e2e/8-http-request-node.cy.ts @@ -1,11 +1,18 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage, NDV } from '../pages'; const workflowPage = new WorkflowPage(); const ndv = new NDV(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('HTTP Request node', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); it('should make a request with a URL and receive a response', () => { diff --git a/cypress/e2e/9-expression-editor-modal.cy.ts b/cypress/e2e/9-expression-editor-modal.cy.ts index 6b0412cc21..6f5ad04b63 100644 --- a/cypress/e2e/9-expression-editor-modal.cy.ts +++ b/cypress/e2e/9-expression-editor-modal.cy.ts @@ -1,13 +1,21 @@ +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; const WorkflowPage = new WorkflowPageClass(); +const email = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Expression editor modal', () => { before(() => { - cy.skipSetup(); + cy.setup({ email, firstName, lastName, password }); }); beforeEach(() => { + cy.signin({ email, password }); WorkflowPage.actions.visit(); WorkflowPage.actions.addInitialNodeToCanvas('Manual'); WorkflowPage.actions.addNodeToCanvas('Hacker News'); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index d0e9ddbaa0..f8092fde33 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -173,34 +173,6 @@ Cypress.Commands.add('inviteUsers', ({ instanceOwner, users }) => { }); }); -Cypress.Commands.add('skipSetup', () => { - const signupPage = new SignupPage(); - const workflowPage = new WorkflowPage(); - const Confirmation = new MessageBox(); - - cy.intercept('GET', signupPage.url).as('setupPage'); - cy.visit(signupPage.url); - cy.wait('@setupPage'); - - signupPage.getters.form().within(() => { - cy.url().then((url) => { - if (url.endsWith(signupPage.url)) { - signupPage.getters.skip().click(); - - Confirmation.getters.header().should('contain.text', 'Skip owner account setup?'); - Confirmation.actions.confirm(); - - // we should be redirected to empty canvas - cy.intercept('GET', '/rest/workflows/new').as('loading'); - cy.url().should('include', workflowPage.url); - cy.wait('@loading'); - } else { - cy.log('User already signed up'); - } - }); - }); -}); - Cypress.Commands.add('resetAll', () => { cy.task('reset'); Cypress.session.clearAllSavedSessions(); diff --git a/cypress/support/index.ts b/cypress/support/index.ts index 7b1b15db2b..608d4095be 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -40,7 +40,6 @@ declare global { setupOwner(payload: SetupPayload): void; inviteUsers(payload: InviteUsersPayload): void; interceptREST(method: string, url: string): Chainable; - skipSetup(): void; resetAll(): void; enableFeature(feature: string): void; disableFeature(feature: string): void; diff --git a/docker/compose/subfolderWithSSL/docker-compose.yml b/docker/compose/subfolderWithSSL/docker-compose.yml index 37a4b25f18..9c4cc247c3 100644 --- a/docker/compose/subfolderWithSSL/docker-compose.yml +++ b/docker/compose/subfolderWithSSL/docker-compose.yml @@ -41,9 +41,6 @@ services: - traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true - traefik.http.middlewares.n8n.headers.STSPreload=true environment: - - N8N_BASIC_AUTH_ACTIVE=true - - N8N_BASIC_AUTH_USER - - N8N_BASIC_AUTH_PASSWORD - N8N_HOST=${DOMAIN_NAME} - N8N_PORT=5678 - N8N_PROTOCOL=https diff --git a/docker/compose/withPostgres/docker-compose.yml b/docker/compose/withPostgres/docker-compose.yml index 9b3ab5b83e..9ee06874b0 100644 --- a/docker/compose/withPostgres/docker-compose.yml +++ b/docker/compose/withPostgres/docker-compose.yml @@ -33,9 +33,6 @@ services: - DB_POSTGRESDB_DATABASE=${POSTGRES_DB} - DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER} - DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD} - - N8N_BASIC_AUTH_ACTIVE=true - - N8N_BASIC_AUTH_USER - - N8N_BASIC_AUTH_PASSWORD ports: - 5678:5678 links: diff --git a/docker/compose/withPostgresAndWorker/docker-compose.yml b/docker/compose/withPostgresAndWorker/docker-compose.yml index 9f947d735d..263c479d71 100644 --- a/docker/compose/withPostgresAndWorker/docker-compose.yml +++ b/docker/compose/withPostgresAndWorker/docker-compose.yml @@ -17,9 +17,6 @@ x-shared: &shared - EXECUTIONS_MODE=queue - QUEUE_BULL_REDIS_HOST=redis - QUEUE_HEALTH_CHECK_ACTIVE=true - - N8N_BASIC_AUTH_ACTIVE=true - - N8N_BASIC_AUTH_USER - - N8N_BASIC_AUTH_PASSWORD links: - postgres - redis diff --git a/docker/images/n8n/README.md b/docker/images/n8n/README.md index 376c4494ab..0233112e55 100644 --- a/docker/images/n8n/README.md +++ b/docker/images/n8n/README.md @@ -71,20 +71,6 @@ docker run -it --rm \ n8n start --tunnel ``` -## Securing n8n - -By default n8n can be accessed by everybody. This is OK if you have it only running -locally but if you deploy it on a server which is accessible from the web you have -to make sure that n8n is protected! -Right now we have very basic protection via basic-auth in place. It can be activated -by setting the following environment variables: - -```text -N8N_BASIC_AUTH_ACTIVE=true -N8N_BASIC_AUTH_USER= -N8N_BASIC_AUTH_PASSWORD= -``` - ## Persist data The workflow data gets by default saved in an SQLite database in the user @@ -181,8 +167,6 @@ The following environment variables support file input: - DB_POSTGRESDB_PORT_FILE - DB_POSTGRESDB_USER_FILE - DB_POSTGRESDB_SCHEMA_FILE -- N8N_BASIC_AUTH_PASSWORD_FILE -- N8N_BASIC_AUTH_USER_FILE ## Example Setup with Lets Encrypt @@ -193,19 +177,19 @@ A basic step by step example setup of n8n with docker-compose and Lets Encrypt i 1. Pull the latest version from the registry - `docker pull docker.n8n.io/n8nio/n8n` + `docker pull docker.n8n.io/n8nio/n8n` 2. Stop the current setup - `sudo docker-compose stop` + `sudo docker-compose stop` 3. Delete it (will only delete the docker-containers, data is stored separately) - `sudo docker-compose rm` + `sudo docker-compose rm` 4. Then start it again - `sudo docker-compose up -d` + `sudo docker-compose up -d` ## Setting Timezone diff --git a/packages/cli/scripts/build.mjs b/packages/cli/scripts/build.mjs index 63a9cb2f6f..4c2b43e74c 100644 --- a/packages/cli/scripts/build.mjs +++ b/packages/cli/scripts/build.mjs @@ -9,12 +9,9 @@ const ROOT_DIR = path.resolve(__dirname, '..'); const SPEC_FILENAME = 'openapi.yml'; const SPEC_THEME_FILENAME = 'swaggerTheme.css'; -const userManagementEnabled = process.env.N8N_USER_MANAGEMENT_DISABLED !== 'true'; const publicApiEnabled = process.env.N8N_PUBLIC_API_DISABLED !== 'true'; -if (userManagementEnabled) { - copyUserManagementEmailTemplates(); -} +copyUserManagementEmailTemplates(); if (publicApiEnabled) { copySwaggerTheme(); diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index f53af09f94..5b44f87b72 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -306,7 +306,6 @@ export interface IDiagnosticInfo { databaseType: DatabaseType; notificationsEnabled: boolean; disableProductionWebhooksOnMainProcess: boolean; - basicAuthActive: boolean; systemInfo: { os: { type?: string; @@ -324,7 +323,6 @@ export interface IDiagnosticInfo { }; deploymentType: string; binaryDataMode: string; - n8n_multi_user_allowed: boolean; smtp_set_up: boolean; ldap_allowed: boolean; saml_enabled: boolean; diff --git a/packages/cli/src/InternalHooks.ts b/packages/cli/src/InternalHooks.ts index 54436694f9..a39c2c7df5 100644 --- a/packages/cli/src/InternalHooks.ts +++ b/packages/cli/src/InternalHooks.ts @@ -75,12 +75,10 @@ export class InternalHooks implements IInternalHooksClass { db_type: diagnosticInfo.databaseType, n8n_version_notifications_enabled: diagnosticInfo.notificationsEnabled, n8n_disable_production_main_process: diagnosticInfo.disableProductionWebhooksOnMainProcess, - n8n_basic_auth_active: diagnosticInfo.basicAuthActive, system_info: diagnosticInfo.systemInfo, execution_variables: diagnosticInfo.executionVariables, n8n_deployment_type: diagnosticInfo.deploymentType, n8n_binary_data_mode: diagnosticInfo.binaryDataMode, - n8n_multi_user_allowed: diagnosticInfo.n8n_multi_user_allowed, smtp_set_up: diagnosticInfo.smtp_set_up, ldap_allowed: diagnosticInfo.ldap_allowed, saml_enabled: diagnosticInfo.saml_enabled, diff --git a/packages/cli/src/Ldap/helpers.ts b/packages/cli/src/Ldap/helpers.ts index 8c51add60b..33ca43916a 100644 --- a/packages/cli/src/Ldap/helpers.ts +++ b/packages/cli/src/Ldap/helpers.ts @@ -12,7 +12,6 @@ import { User } from '@db/entities/User'; import { AuthIdentity } from '@db/entities/AuthIdentity'; import { RoleRepository } from '@db/repositories'; import type { AuthProviderSyncHistory } from '@db/entities/AuthProviderSyncHistory'; -import { isUserManagementEnabled } from '@/UserManagement/UserManagementHelper'; import { LdapManager } from './LdapManager.ee'; import { @@ -37,10 +36,7 @@ import { InternalServerError } from '../ResponseHelper'; /** * Check whether the LDAP feature is disabled in the instance */ -export const isLdapEnabled = (): boolean => { - const license = Container.get(License); - return isUserManagementEnabled() && license.isLdapEnabled(); -}; +export const isLdapEnabled = () => Container.get(License).isLdapEnabled(); /** * Check whether the LDAP feature is enabled in the instance diff --git a/packages/cli/src/ResponseHelper.ts b/packages/cli/src/ResponseHelper.ts index 907895914f..312990f683 100644 --- a/packages/cli/src/ResponseHelper.ts +++ b/packages/cli/src/ResponseHelper.ts @@ -87,17 +87,6 @@ export class ServiceUnavailableError extends ResponseError { } } -export function basicAuthAuthorizationError(resp: Response, realm: string, message?: string) { - resp.statusCode = 401; - resp.setHeader('WWW-Authenticate', `Basic realm="${realm}"`); - resp.json({ code: resp.statusCode, message }); -} - -export function jwtAuthAuthorizationError(resp: Response, message?: string) { - resp.statusCode = 403; - resp.json({ code: resp.statusCode, message }); -} - export function sendSuccessResponse( res: Response, data: any, diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 279e414bd0..f264a3944c 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -105,7 +105,6 @@ import { getInstanceBaseUrl, isEmailSetUp, isSharingEnabled, - isUserManagementEnabled, whereClause, } from '@/UserManagement/UserManagementHelper'; import { UserManagementMailer } from '@/UserManagement/email'; @@ -145,8 +144,6 @@ import { } from './Ldap/helpers'; import { AbstractServer } from './AbstractServer'; import { configureMetrics } from './metrics'; -import { setupBasicAuth } from './middlewares/basicAuth'; -import { setupExternalJWTAuth } from './middlewares/externalJWTAuth'; import { PostHogClient } from './posthog'; import { eventBus } from './eventbus'; import { Container } from 'typedi'; @@ -261,11 +258,7 @@ export class Server extends AbstractServer { config.getEnv('personalization.enabled') && config.getEnv('diagnostics.enabled'), defaultLocale: config.getEnv('defaultLocale'), userManagement: { - enabled: isUserManagementEnabled(), - showSetupOnFirstLoad: - config.getEnv('userManagement.disabled') === false && - config.getEnv('userManagement.isInstanceOwnerSetUp') === false && - config.getEnv('userManagement.skipInstanceOwnerSetup') === false, + showSetupOnFirstLoad: config.getEnv('userManagement.isInstanceOwnerSetUp') === false, smtpSetup: isEmailSetUp(), authenticationMethod: getCurrentAuthenticationMethod(), }, @@ -349,7 +342,6 @@ export class Server extends AbstractServer { const cpus = os.cpus(); const binaryDataConfig = config.getEnv('binaryDataManager'); const diagnosticInfo: IDiagnosticInfo = { - basicAuthActive: config.getEnv('security.basicAuth.active'), databaseType: config.getEnv('database.type'), disableProductionWebhooksOnMainProcess: config.getEnv( 'endpoints.disableProductionWebhooksOnMainProcess', @@ -385,7 +377,6 @@ export class Server extends AbstractServer { }, deploymentType: config.getEnv('deployment.type'), binaryDataMode: binaryDataConfig.mode, - n8n_multi_user_allowed: isUserManagementEnabled(), smtp_set_up: config.getEnv('userManagement.emails.mode') === 'smtp', ldap_allowed: isLdapCurrentAuthenticationMethod(), saml_enabled: isSamlCurrentAuthenticationMethod(), @@ -414,12 +405,9 @@ export class Server extends AbstractServer { getSettingsForFrontend(): IN8nUISettings { // refresh user management status Object.assign(this.frontendSettings.userManagement, { - enabled: isUserManagementEnabled(), authenticationMethod: getCurrentAuthenticationMethod(), showSetupOnFirstLoad: - config.getEnv('userManagement.disabled') === false && config.getEnv('userManagement.isInstanceOwnerSetUp') === false && - config.getEnv('userManagement.skipInstanceOwnerSetup') === false && config.getEnv('deployment.type').startsWith('desktop_') === false, }); @@ -461,7 +449,7 @@ export class Server extends AbstractServer { private registerControllers(ignoredEndpoints: Readonly) { const { app, externalHooks, activeWorkflowRunner, nodeTypes } = this; const repositories = Db.collections; - setupAuthMiddlewares(app, ignoredEndpoints, this.restEndpoint, repositories.User); + setupAuthMiddlewares(app, ignoredEndpoints, this.restEndpoint); const logger = LoggerProxy; const internalHooks = Container.get(InternalHooks); @@ -552,19 +540,6 @@ export class Server extends AbstractServer { `REST endpoint cannot be set to any of these values: ${ignoredEndpoints.join()} `, ); - // eslint-disable-next-line no-useless-escape - const authIgnoreRegex = new RegExp(`^\/(${ignoredEndpoints.join('|')})\/?.*$`); - - // Check for basic auth credentials if activated - if (config.getEnv('security.basicAuth.active')) { - setupBasicAuth(this.app, config, authIgnoreRegex); - } - - // Check for and validate JWT if configured - if (config.getEnv('security.jwtAuth.active')) { - setupExternalJWTAuth(this.app, config, authIgnoreRegex); - } - // ---------------------------------------- // Public API // ---------------------------------------- @@ -578,7 +553,7 @@ export class Server extends AbstractServer { this.app.use(cookieParser()); const { restEndpoint, app } = this; - setupPushHandler(restEndpoint, app, isUserManagementEnabled()); + setupPushHandler(restEndpoint, app); // Make sure that Vue history mode works properly this.app.use( diff --git a/packages/cli/src/UserManagement/UserManagementHelper.ts b/packages/cli/src/UserManagement/UserManagementHelper.ts index e299544840..3494bef2bc 100644 --- a/packages/cli/src/UserManagement/UserManagementHelper.ts +++ b/packages/cli/src/UserManagement/UserManagementHelper.ts @@ -36,26 +36,8 @@ export function isEmailSetUp(): boolean { return smtp && host && user && pass; } -export function isUserManagementEnabled(): boolean { - // This can be simplified but readability is more important here - - if (config.getEnv('userManagement.isInstanceOwnerSetUp')) { - // Short circuit - if owner is set up, UM cannot be disabled. - // Users must reset their instance in order to do so. - return true; - } - - // UM is disabled for desktop by default - if (config.getEnv('deployment.type').startsWith('desktop_')) { - return false; - } - - return config.getEnv('userManagement.disabled') ? false : true; -} - export function isSharingEnabled(): boolean { - const license = Container.get(License); - return isUserManagementEnabled() && license.isSharingEnabled(); + return Container.get(License).isSharingEnabled(); } export async function getRoleId(scope: Role['scope'], name: Role['name']): Promise { diff --git a/packages/cli/src/api/e2e.api.ts b/packages/cli/src/api/e2e.api.ts index efecd7c34c..b0e11e23ed 100644 --- a/packages/cli/src/api/e2e.api.ts +++ b/packages/cli/src/api/e2e.api.ts @@ -95,7 +95,7 @@ const setupUserManagement = async () => { `INSERT INTO user (id, globalRoleId) values ("${uuid()}", ${instanceOwnerRole[0].insertId})`, ); await connection.query( - "INSERT INTO \"settings\" (key, value, loadOnStartup) values ('userManagement.isInstanceOwnerSetUp', 'false', true), ('userManagement.skipInstanceOwnerSetup', 'false', true)", + "INSERT INTO \"settings\" (key, value, loadOnStartup) values ('userManagement.isInstanceOwnerSetUp', 'false', true)", ); config.set('userManagement.isInstanceOwnerSetUp', false); diff --git a/packages/cli/src/audit/risks/instance.risk.ts b/packages/cli/src/audit/risks/instance.risk.ts index 6d75acbd98..d60859f38f 100644 --- a/packages/cli/src/audit/risks/instance.risk.ts +++ b/packages/cli/src/audit/risks/instance.risk.ts @@ -18,31 +18,17 @@ import { isApiEnabled } from '@/PublicApi'; function getSecuritySettings() { if (config.getEnv('deployment.type') === 'cloud') return null; - const userManagementEnabled = !config.getEnv('userManagement.disabled'); - const basicAuthActive = config.getEnv('security.basicAuth.active'); - const jwtAuthActive = config.getEnv('security.jwtAuth.active'); - - const isInstancePubliclyAccessible = !userManagementEnabled && !basicAuthActive && !jwtAuthActive; - const settings: Record = {}; - if (isInstancePubliclyAccessible) { - settings.publiclyAccessibleInstance = - 'Important! Your n8n instance is publicly accessible. Any third party who knows your instance URL can access your data.'.toUpperCase(); - } - settings.features = { communityPackagesEnabled: config.getEnv('nodes.communityPackages.enabled'), versionNotificationsEnabled: config.getEnv('versionNotifications.enabled'), templatesEnabled: config.getEnv('templates.enabled'), publicApiEnabled: isApiEnabled(), - userManagementEnabled, }; settings.auth = { authExcludeEndpoints: config.getEnv('security.excludeEndpoints') || 'none', - basicAuthActive, - jwtAuthActive, }; settings.nodes = { diff --git a/packages/cli/src/commands/BaseCommand.ts b/packages/cli/src/commands/BaseCommand.ts index e9d387c440..d748d06f65 100644 --- a/packages/cli/src/commands/BaseCommand.ts +++ b/packages/cli/src/commands/BaseCommand.ts @@ -9,7 +9,7 @@ import { getLogger } from '@/Logger'; import config from '@/config'; import * as Db from '@/Db'; import * as CrashJournal from '@/CrashJournal'; -import { USER_MANAGEMENT_DOCS_URL, inTest } from '@/constants'; +import { inTest } from '@/constants'; import { CredentialTypes } from '@/CredentialTypes'; import { CredentialsOverwrites } from '@/CredentialsOverwrites'; import { initErrorHandling } from '@/ErrorReporting'; @@ -78,24 +78,6 @@ export abstract class BaseCommand extends Command { ); } - if (process.env.N8N_BASIC_AUTH_ACTIVE === 'true') { - LoggerProxy.warn( - `Basic auth has been deprecated and will be removed in a future version of n8n. For authentication, please consider User Management. To learn more: ${USER_MANAGEMENT_DOCS_URL}`, - ); - } - - if (process.env.N8N_JWT_AUTH_ACTIVE === 'true') { - LoggerProxy.warn( - `JWT auth has been deprecated and will be removed in a future version of n8n. For authentication, please consider User Management. To learn more: ${USER_MANAGEMENT_DOCS_URL}`, - ); - } - - if (process.env.N8N_USER_MANAGEMENT_DISABLED === 'true') { - LoggerProxy.warn( - `User Management will be mandatory in a future version of n8n. Please set up the instance owner. To learn more: ${USER_MANAGEMENT_DOCS_URL}`, - ); - } - this.instanceId = this.userSettings.instanceId ?? ''; await Container.get(PostHogClient).init(this.instanceId); await Container.get(InternalHooks).init(this.instanceId); diff --git a/packages/cli/src/commands/user-management/reset.ts b/packages/cli/src/commands/user-management/reset.ts index 66c8de0117..eec5e1784a 100644 --- a/packages/cli/src/commands/user-management/reset.ts +++ b/packages/cli/src/commands/user-management/reset.ts @@ -56,10 +56,6 @@ export class Reset extends BaseCommand { { key: 'userManagement.isInstanceOwnerSetUp' }, { value: 'false' }, ); - await Db.collections.Settings.update( - { key: 'userManagement.skipInstanceOwnerSetup' }, - { value: 'false' }, - ); this.logger.info('Successfully reset the database to default user state.'); } diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts index d7744cee47..ae36bc5d25 100644 --- a/packages/cli/src/config/schema.ts +++ b/packages/cli/src/config/schema.ts @@ -492,82 +492,6 @@ export const schema = { default: '', env: 'N8N_AUTH_EXCLUDE_ENDPOINTS', }, - basicAuth: { - active: { - format: 'Boolean', - default: false, - env: 'N8N_BASIC_AUTH_ACTIVE', - doc: '[DEPRECATED] If basic auth should be activated for editor and REST-API', - }, - user: { - format: String, - default: '', - env: 'N8N_BASIC_AUTH_USER', - doc: '[DEPRECATED] The name of the basic auth user', - }, - password: { - format: String, - default: '', - env: 'N8N_BASIC_AUTH_PASSWORD', - doc: '[DEPRECATED] The password of the basic auth user', - }, - hash: { - format: 'Boolean', - default: false, - env: 'N8N_BASIC_AUTH_HASH', - doc: '[DEPRECATED] If password for basic auth is hashed', - }, - }, - jwtAuth: { - active: { - format: 'Boolean', - default: false, - env: 'N8N_JWT_AUTH_ACTIVE', - doc: '[DEPRECATED] If JWT auth should be activated for editor and REST-API', - }, - jwtHeader: { - format: String, - default: '', - env: 'N8N_JWT_AUTH_HEADER', - doc: '[DEPRECATED] The request header containing a signed JWT', - }, - jwtHeaderValuePrefix: { - format: String, - default: '', - env: 'N8N_JWT_AUTH_HEADER_VALUE_PREFIX', - doc: '[DEPRECATED] The request header value prefix to strip (optional)', - }, - jwksUri: { - format: String, - default: '', - env: 'N8N_JWKS_URI', - doc: '[DEPRECATED] The URI to fetch JWK Set for JWT authentication', - }, - jwtIssuer: { - format: String, - default: '', - env: 'N8N_JWT_ISSUER', - doc: '[DEPRECATED] JWT issuer to expect (optional)', - }, - jwtNamespace: { - format: String, - default: '', - env: 'N8N_JWT_NAMESPACE', - doc: '[DEPRECATED] JWT namespace to expect (optional)', - }, - jwtAllowedTenantKey: { - format: String, - default: '', - env: 'N8N_JWT_ALLOWED_TENANT_KEY', - doc: '[DEPRECATED] JWT tenant key name to inspect within JWT namespace (optional)', - }, - jwtAllowedTenant: { - format: String, - default: '', - env: 'N8N_JWT_ALLOWED_TENANT', - doc: '[DEPRECATED] JWT tenant to allow (optional)', - }, - }, }, endpoints: { @@ -726,12 +650,6 @@ export const schema = { }, userManagement: { - disabled: { - doc: '[DEPRECATED] Disable user management and hide it completely.', - format: Boolean, - default: false, - env: 'N8N_USER_MANAGEMENT_DISABLED', - }, jwtSecret: { doc: 'Set a specific JWT secret (optional - n8n can generate one)', // Generated @ start.ts format: String, @@ -744,12 +662,6 @@ export const schema = { format: Boolean, default: false, }, - skipInstanceOwnerSetup: { - // n8n loads this setting from DB on startup - doc: 'Whether to hide the prompt the first time n8n starts with UM enabled', - format: Boolean, - default: false, - }, emails: { mode: { doc: 'How to send emails', diff --git a/packages/cli/src/config/types.d.ts b/packages/cli/src/config/types.d.ts index 4dfd71a6b7..e733219634 100644 --- a/packages/cli/src/config/types.d.ts +++ b/packages/cli/src/config/types.d.ts @@ -80,7 +80,6 @@ type ExceptionPaths = { 'nodes.exclude': string[] | undefined; 'nodes.include': string[] | undefined; 'userManagement.isInstanceOwnerSetUp': boolean; - 'userManagement.skipInstanceOwnerSetup': boolean; }; // ----------------------------------- diff --git a/packages/cli/src/constants.ts b/packages/cli/src/constants.ts index e9969dffce..d13ae9a26b 100644 --- a/packages/cli/src/constants.ts +++ b/packages/cli/src/constants.ts @@ -86,6 +86,3 @@ export const enum LICENSE_QUOTAS { } export const CREDENTIAL_BLANKING_VALUE = '__n8n_BLANK_VALUE_e5362baf-c777-4d57-a609-6eaf1f9e87f6'; - -export const USER_MANAGEMENT_DOCS_URL = - 'https://docs.n8n.io/hosting/authentication/user-management-self-hosted'; diff --git a/packages/cli/src/controllers/owner.controller.ts b/packages/cli/src/controllers/owner.controller.ts index ae8745a9af..217cda5e7a 100644 --- a/packages/cli/src/controllers/owner.controller.ts +++ b/packages/cli/src/controllers/owner.controller.ts @@ -148,19 +148,4 @@ export class OwnerController { return sanitizeUser(owner); } - - /** - * Persist that the instance owner setup has been skipped - */ - @Post('/skip-setup') - async skipSetup() { - await this.settingsRepository.update( - { key: 'userManagement.skipInstanceOwnerSetup' }, - { value: JSON.stringify(true) }, - ); - - this.config.set('userManagement.skipInstanceOwnerSetup', true); - - return { success: true }; - } } diff --git a/packages/cli/src/controllers/users.controller.ts b/packages/cli/src/controllers/users.controller.ts index e68f869968..4b5724b6bc 100644 --- a/packages/cli/src/controllers/users.controller.ts +++ b/packages/cli/src/controllers/users.controller.ts @@ -32,7 +32,6 @@ import type { import type { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner'; import { AuthIdentity } from '@db/entities/AuthIdentity'; import type { PostHogClient } from '@/posthog'; -import { userManagementEnabledMiddleware } from '../middlewares/userManagementEnabled'; import { isSamlLicensedAndEnabled } from '../sso/saml/samlHelpers'; import type { RoleRepository, @@ -106,7 +105,7 @@ export class UsersController { /** * Send email invite(s) to one or multiple users and create user shell(s). */ - @Post('/', { middlewares: [userManagementEnabledMiddleware] }) + @Post('/') async sendEmailInvites(req: UserRequest.Invite) { if (isSamlLicensedAndEnabled()) { this.logger.debug( diff --git a/packages/cli/src/databases/migrations/mysqldb/1681134145997-RemoveSkipOwnerSetup.ts b/packages/cli/src/databases/migrations/mysqldb/1681134145997-RemoveSkipOwnerSetup.ts new file mode 100644 index 0000000000..c756b5839a --- /dev/null +++ b/packages/cli/src/databases/migrations/mysqldb/1681134145997-RemoveSkipOwnerSetup.ts @@ -0,0 +1,9 @@ +import type { IrreversibleMigration, MigrationContext } from '@db/types'; + +export class RemoveSkipOwnerSetup1681134145997 implements IrreversibleMigration { + async up({ queryRunner, tablePrefix }: MigrationContext) { + await queryRunner.query( + `DELETE FROM ${tablePrefix}settings WHERE \`key\` = 'userManagement.skipInstanceOwnerSetup';`, + ); + } +} diff --git a/packages/cli/src/databases/migrations/mysqldb/index.ts b/packages/cli/src/databases/migrations/mysqldb/index.ts index ac84b759e2..c8a0d39cd7 100644 --- a/packages/cli/src/databases/migrations/mysqldb/index.ts +++ b/packages/cli/src/databases/migrations/mysqldb/index.ts @@ -40,6 +40,7 @@ import { CreateVariables1677501636753 } from './1677501636753-CreateVariables'; import { AddUserActivatedProperty1681134145996 } from './1681134145996-AddUserActivatedProperty'; import { MigrateIntegerKeysToString1690000000001 } from './1690000000001-MigrateIntegerKeysToString'; import { SeparateExecutionData1690000000030 } from './1690000000030-SeparateExecutionData'; +import { RemoveSkipOwnerSetup1681134145997 } from './1681134145997-RemoveSkipOwnerSetup'; export const mysqlMigrations: Migration[] = [ InitialMigration1588157391238, @@ -83,4 +84,5 @@ export const mysqlMigrations: Migration[] = [ AddUserActivatedProperty1681134145996, MigrateIntegerKeysToString1690000000001, SeparateExecutionData1690000000030, + RemoveSkipOwnerSetup1681134145997, ]; diff --git a/packages/cli/src/databases/migrations/postgresdb/1681134145997-RemoveSkipOwnerSetup.ts b/packages/cli/src/databases/migrations/postgresdb/1681134145997-RemoveSkipOwnerSetup.ts new file mode 100644 index 0000000000..958cf30199 --- /dev/null +++ b/packages/cli/src/databases/migrations/postgresdb/1681134145997-RemoveSkipOwnerSetup.ts @@ -0,0 +1,9 @@ +import type { IrreversibleMigration, MigrationContext } from '@db/types'; + +export class RemoveSkipOwnerSetup1681134145997 implements IrreversibleMigration { + async up({ queryRunner, tablePrefix }: MigrationContext) { + await queryRunner.query( + `DELETE FROM ${tablePrefix}settings WHERE key = 'userManagement.skipInstanceOwnerSetup';`, + ); + } +} diff --git a/packages/cli/src/databases/migrations/postgresdb/index.ts b/packages/cli/src/databases/migrations/postgresdb/index.ts index 95c025acf6..460c33213c 100644 --- a/packages/cli/src/databases/migrations/postgresdb/index.ts +++ b/packages/cli/src/databases/migrations/postgresdb/index.ts @@ -38,6 +38,7 @@ import { CreateVariables1677501636754 } from './1677501636754-CreateVariables'; import { AddUserActivatedProperty1681134145996 } from './1681134145996-AddUserActivatedProperty'; import { MigrateIntegerKeysToString1690000000000 } from './1690000000000-MigrateIntegerKeysToString'; import { SeparateExecutionData1690000000020 } from './1690000000020-SeparateExecutionData'; +import { RemoveSkipOwnerSetup1681134145997 } from './1681134145997-RemoveSkipOwnerSetup'; export const postgresMigrations: Migration[] = [ InitialMigration1587669153312, @@ -79,4 +80,5 @@ export const postgresMigrations: Migration[] = [ AddUserActivatedProperty1681134145996, MigrateIntegerKeysToString1690000000000, SeparateExecutionData1690000000020, + RemoveSkipOwnerSetup1681134145997, ]; diff --git a/packages/cli/src/databases/migrations/sqlite/1681134145997-RemoveSkipOwnerSetup.ts b/packages/cli/src/databases/migrations/sqlite/1681134145997-RemoveSkipOwnerSetup.ts new file mode 100644 index 0000000000..4d8f7d47c6 --- /dev/null +++ b/packages/cli/src/databases/migrations/sqlite/1681134145997-RemoveSkipOwnerSetup.ts @@ -0,0 +1,9 @@ +import type { IrreversibleMigration, MigrationContext } from '@db/types'; + +export class RemoveSkipOwnerSetup1681134145997 implements IrreversibleMigration { + async up({ queryRunner, tablePrefix }: MigrationContext) { + await queryRunner.query( + `DELETE FROM "${tablePrefix}settings" WHERE key = 'userManagement.skipInstanceOwnerSetup';`, + ); + } +} diff --git a/packages/cli/src/databases/migrations/sqlite/index.ts b/packages/cli/src/databases/migrations/sqlite/index.ts index bedd8cd14a..b0a98fa10b 100644 --- a/packages/cli/src/databases/migrations/sqlite/index.ts +++ b/packages/cli/src/databases/migrations/sqlite/index.ts @@ -37,6 +37,7 @@ import { CreateVariables1677501636752 } from './1677501636752-CreateVariables'; import { AddUserActivatedProperty1681134145996 } from './1681134145996-AddUserActivatedProperty'; import { MigrateIntegerKeysToString1690000000002 } from './1690000000002-MigrateIntegerKeysToString'; import { SeparateExecutionData1690000000010 } from './1690000000010-SeparateExecutionData'; +import { RemoveSkipOwnerSetup1681134145997 } from './1681134145997-RemoveSkipOwnerSetup'; const sqliteMigrations: Migration[] = [ InitialMigration1588102412422, @@ -77,6 +78,7 @@ const sqliteMigrations: Migration[] = [ AddUserActivatedProperty1681134145996, MigrateIntegerKeysToString1690000000002, SeparateExecutionData1690000000010, + RemoveSkipOwnerSetup1681134145997, ]; export { sqliteMigrations }; diff --git a/packages/cli/src/middlewares/auth.ts b/packages/cli/src/middlewares/auth.ts index e28272766b..8b3a53cca6 100644 --- a/packages/cli/src/middlewares/auth.ts +++ b/packages/cli/src/middlewares/auth.ts @@ -10,7 +10,6 @@ import type { AuthenticatedRequest } from '@/requests'; import config from '@/config'; import { AUTH_COOKIE_NAME, EDITOR_UI_DIST_DIR } from '@/constants'; import { issueCookie, resolveJwtContent } from '@/auth/jwt'; -import { isUserManagementEnabled } from '@/UserManagement/UserManagementHelper'; import type { UserRepository } from '@db/repositories'; import { canSkipAuth } from '@/decorators/registerController'; @@ -19,7 +18,7 @@ const jwtFromRequest = (req: Request) => { return (req.cookies?.[AUTH_COOKIE_NAME] as string | undefined) ?? null; }; -const jwtAuth = (): RequestHandler => { +const userManagementJwtAuth = (): RequestHandler => { const jwtStrategy = new Strategy( { jwtFromRequest, @@ -79,11 +78,10 @@ export const setupAuthMiddlewares = ( app: Application, ignoredEndpoints: Readonly, restEndpoint: string, - userRepository: UserRepository, ) => { // needed for testing; not adding overhead since it directly returns if req.cookies exists app.use(cookieParser()); - app.use(jwtAuth()); + app.use(userManagementJwtAuth()); app.use(async (req: Request, res: Response, next: NextFunction) => { if ( @@ -101,15 +99,6 @@ export const setupAuthMiddlewares = ( return next(); } - // skip authentication if user management is disabled - if (!isUserManagementEnabled()) { - req.user = await userRepository.findOneOrFail({ - relations: ['globalRole'], - where: {}, - }); - return next(); - } - return passportMiddleware(req, res, next); }); diff --git a/packages/cli/src/middlewares/basicAuth.ts b/packages/cli/src/middlewares/basicAuth.ts deleted file mode 100644 index bdf28b5a0e..0000000000 --- a/packages/cli/src/middlewares/basicAuth.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { Application } from 'express'; -import basicAuth from 'basic-auth'; -// IMPORTANT! Do not switch to anther bcrypt library unless really necessary and -// tested with all possible systems like Windows, Alpine on ARM, FreeBSD, ... -import { compare } from 'bcryptjs'; -import type { Config } from '@/config'; -import { basicAuthAuthorizationError } from '@/ResponseHelper'; - -export const setupBasicAuth = (app: Application, config: Config, authIgnoreRegex: RegExp) => { - const basicAuthUser = config.getEnv('security.basicAuth.user'); - if (basicAuthUser === '') { - throw new Error('Basic auth is activated but no user got defined. Please set one!'); - } - - const basicAuthPassword = config.getEnv('security.basicAuth.password'); - if (basicAuthPassword === '') { - throw new Error('Basic auth is activated but no password got defined. Please set one!'); - } - - const basicAuthHashEnabled = config.getEnv('security.basicAuth.hash'); - - let validPassword: null | string = null; - - app.use(async (req, res, next) => { - // Skip basic auth for a few listed endpoints or when instance owner has been setup - if (authIgnoreRegex.exec(req.url) || config.getEnv('userManagement.isInstanceOwnerSetUp')) { - return next(); - } - const realm = 'n8n - Editor UI'; - const basicAuthData = basicAuth(req); - - if (basicAuthData === undefined) { - // Authorization data is missing - return basicAuthAuthorizationError(res, realm, 'Authorization is required!'); - } - - if (basicAuthData.name === basicAuthUser) { - if (basicAuthHashEnabled) { - if (validPassword === null && (await compare(basicAuthData.pass, basicAuthPassword))) { - // Password is valid so save for future requests - validPassword = basicAuthData.pass; - } - - if (validPassword === basicAuthData.pass && validPassword !== null) { - // Provided hash is correct - return next(); - } - } else if (basicAuthData.pass === basicAuthPassword) { - // Provided password is correct - return next(); - } - } - - // Provided authentication data is wrong - return basicAuthAuthorizationError(res, realm, 'Authorization data is wrong!'); - }); -}; diff --git a/packages/cli/src/middlewares/externalJWTAuth.ts b/packages/cli/src/middlewares/externalJWTAuth.ts deleted file mode 100644 index 8d769933b3..0000000000 --- a/packages/cli/src/middlewares/externalJWTAuth.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { Application } from 'express'; -import jwt from 'jsonwebtoken'; -import jwks from 'jwks-rsa'; -import type { Config } from '@/config'; -import { jwtAuthAuthorizationError } from '@/ResponseHelper'; - -export const setupExternalJWTAuth = (app: Application, config: Config, authIgnoreRegex: RegExp) => { - const jwtAuthHeader = config.getEnv('security.jwtAuth.jwtHeader'); - if (jwtAuthHeader === '') { - throw new Error('JWT auth is activated but no request header was defined. Please set one!'); - } - - const jwksUri = config.getEnv('security.jwtAuth.jwksUri'); - if (jwksUri === '') { - throw new Error('JWT auth is activated but no JWK Set URI was defined. Please set one!'); - } - - const jwtHeaderValuePrefix = config.getEnv('security.jwtAuth.jwtHeaderValuePrefix'); - const jwtIssuer = config.getEnv('security.jwtAuth.jwtIssuer'); - const jwtNamespace = config.getEnv('security.jwtAuth.jwtNamespace'); - const jwtAllowedTenantKey = config.getEnv('security.jwtAuth.jwtAllowedTenantKey'); - const jwtAllowedTenant = config.getEnv('security.jwtAuth.jwtAllowedTenant'); - - // eslint-disable-next-line no-inner-declarations - function isTenantAllowed(decodedToken: object): boolean { - if (jwtNamespace === '' || jwtAllowedTenantKey === '' || jwtAllowedTenant === '') { - return true; - } - - for (const [k, v] of Object.entries(decodedToken)) { - if (k === jwtNamespace) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - for (const [kn, kv] of Object.entries(v)) { - if (kn === jwtAllowedTenantKey && kv === jwtAllowedTenant) { - return true; - } - } - } - } - - return false; - } - - // eslint-disable-next-line consistent-return - app.use((req, res, next) => { - if (authIgnoreRegex.exec(req.url)) { - return next(); - } - - let token = req.header(jwtAuthHeader) as string; - if (token === undefined || token === '') { - return jwtAuthAuthorizationError(res, 'Missing token'); - } - - if (jwtHeaderValuePrefix !== '' && token.startsWith(jwtHeaderValuePrefix)) { - token = token.replace(`${jwtHeaderValuePrefix} `, '').trimStart(); - } - - const jwkClient = jwks({ cache: true, jwksUri }); - const getKey: jwt.GetPublicKeyOrSecret = (header, callbackFn) => { - // eslint-disable-next-line @typescript-eslint/no-throw-literal - if (!header.kid) throw jwtAuthAuthorizationError(res, 'No JWT key found'); - jwkClient.getSigningKey(header.kid, (error, key) => { - // eslint-disable-next-line @typescript-eslint/no-throw-literal - if (error) throw jwtAuthAuthorizationError(res, error.message); - callbackFn(null, key?.getPublicKey()); - }); - }; - - const jwtVerifyOptions: jwt.VerifyOptions = { - issuer: jwtIssuer !== '' ? jwtIssuer : undefined, - ignoreExpiration: false, - }; - - jwt.verify(token, getKey, jwtVerifyOptions, (error: jwt.VerifyErrors, decoded: object) => { - if (error) { - jwtAuthAuthorizationError(res, 'Invalid token'); - } else if (!isTenantAllowed(decoded)) { - jwtAuthAuthorizationError(res, 'Tenant not allowed'); - } else { - next(); - } - }); - }); -}; diff --git a/packages/cli/src/middlewares/userManagementEnabled.ts b/packages/cli/src/middlewares/userManagementEnabled.ts deleted file mode 100644 index c1f3c58c6f..0000000000 --- a/packages/cli/src/middlewares/userManagementEnabled.ts +++ /dev/null @@ -1,12 +0,0 @@ -import type { RequestHandler } from 'express'; -import { LoggerProxy } from 'n8n-workflow'; -import { isUserManagementEnabled } from '../UserManagement/UserManagementHelper'; - -export const userManagementEnabledMiddleware: RequestHandler = (req, res, next) => { - if (isUserManagementEnabled()) { - next(); - } else { - LoggerProxy.debug('Request failed because user management is disabled'); - res.status(400).json({ status: 'error', message: 'User management is disabled' }); - } -}; diff --git a/packages/cli/src/push/index.ts b/packages/cli/src/push/index.ts index 63615605d6..3fec3edcf8 100644 --- a/packages/cli/src/push/index.ts +++ b/packages/cli/src/push/index.ts @@ -57,11 +57,7 @@ export const setupPushServer = (restEndpoint: string, server: Server, app: Appli } }; -export const setupPushHandler = ( - restEndpoint: string, - app: Application, - isUserManagementEnabled: boolean, -) => { +export const setupPushHandler = (restEndpoint: string, app: Application) => { const endpoint = `/${restEndpoint}/push`; const pushValidationMiddleware: RequestHandler = async ( @@ -83,20 +79,19 @@ export const setupPushHandler = ( } // Handle authentication - if (isUserManagementEnabled) { - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - const authCookie: string = req.cookies?.[AUTH_COOKIE_NAME] ?? ''; - await resolveJwt(authCookie); - } catch (error) { - if (ws) { - ws.send(`Unauthorized: ${(error as Error).message}`); - ws.close(401); - } else { - res.status(401).send('Unauthorized'); - } - return; + + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access + const authCookie: string = req.cookies?.[AUTH_COOKIE_NAME] ?? ''; + await resolveJwt(authCookie); + } catch (error) { + if (ws) { + ws.send(`Unauthorized: ${(error as Error).message}`); + ws.close(401); + } else { + res.status(401).send('Unauthorized'); } + return; } next(); diff --git a/packages/cli/src/sso/saml/samlHelpers.ts b/packages/cli/src/sso/saml/samlHelpers.ts index 0672965deb..2de5438624 100644 --- a/packages/cli/src/sso/saml/samlHelpers.ts +++ b/packages/cli/src/sso/saml/samlHelpers.ts @@ -6,7 +6,7 @@ import { User } from '@db/entities/User'; import { RoleRepository } from '@db/repositories'; import { License } from '@/License'; import { AuthError, InternalServerError } from '@/ResponseHelper'; -import { hashPassword, isUserManagementEnabled } from '@/UserManagement/UserManagementHelper'; +import { hashPassword } from '@/UserManagement/UserManagementHelper'; import type { SamlPreferences } from './types/samlPreferences'; import type { SamlUserAttributes } from './types/samlUserAttributes'; import type { FlowResult } from 'samlify/types/src/flow'; @@ -52,10 +52,7 @@ export function setSamlLoginLabel(label: string): void { config.set(SAML_LOGIN_LABEL, label); } -export function isSamlLicensed(): boolean { - const license = Container.get(License); - return isUserManagementEnabled() && license.isSamlEnabled(); -} +export const isSamlLicensed = () => Container.get(License).isSamlEnabled(); export function isSamlLicensedAndEnabled(): boolean { return isSamlLoginEnabled() && isSamlLicensed() && isSamlCurrentAuthenticationMethod(); diff --git a/packages/cli/test/integration/audit/instance.risk.test.ts b/packages/cli/test/integration/audit/instance.risk.test.ts index 89411c1d0d..73d04666ba 100644 --- a/packages/cli/test/integration/audit/instance.risk.test.ts +++ b/packages/cli/test/integration/audit/instance.risk.test.ts @@ -244,12 +244,9 @@ test('should report security settings', async () => { versionNotificationsEnabled: true, templatesEnabled: true, publicApiEnabled: false, - userManagementEnabled: true, }, auth: { authExcludeEndpoints: 'none', - basicAuthActive: false, - jwtAuthActive: false, }, nodes: { nodesExclude: 'none', nodesInclude: 'none' }, telemetry: { diagnosticsEnabled: true }, diff --git a/packages/cli/test/integration/eventbus.test.ts b/packages/cli/test/integration/eventbus.test.ts index d6c1c84c8f..5edee16f71 100644 --- a/packages/cli/test/integration/eventbus.test.ts +++ b/packages/cli/test/integration/eventbus.test.ts @@ -106,7 +106,6 @@ beforeAll(async () => { await utils.initConfigFile(); config.set('eventBus.logWriter.logBaseName', 'n8n-test-logwriter'); config.set('eventBus.logWriter.keepLogCount', 1); - config.set('userManagement.disabled', false); config.set('userManagement.isInstanceOwnerSetUp', true); await eventBus.initialize(); diff --git a/packages/cli/test/integration/ldap/ldap.api.test.ts b/packages/cli/test/integration/ldap/ldap.api.test.ts index 0e9688c639..aa43287e27 100644 --- a/packages/cli/test/integration/ldap/ldap.api.test.ts +++ b/packages/cli/test/integration/ldap/ldap.api.test.ts @@ -77,7 +77,6 @@ beforeEach(async () => { jest.mock('@/telemetry'); - config.set('userManagement.disabled', false); config.set('userManagement.isInstanceOwnerSetUp', true); config.set('userManagement.emails.mode', ''); }); diff --git a/packages/cli/test/integration/owner.api.test.ts b/packages/cli/test/integration/owner.api.test.ts index 8b59df68d7..aa02fa4d20 100644 --- a/packages/cli/test/integration/owner.api.test.ts +++ b/packages/cli/test/integration/owner.api.test.ts @@ -169,19 +169,3 @@ describe('POST /owner/setup', () => { ); }); }); - -describe('POST /owner/skip-setup', () => { - test('should persist skipping setup to the DB', async () => { - const response = await authOwnerShellAgent.post('/owner/skip-setup').send(); - - expect(response.statusCode).toBe(200); - - const skipConfig = config.getEnv('userManagement.skipInstanceOwnerSetup'); - expect(skipConfig).toBe(true); - - const { value } = await Db.collections.Settings.findOneByOrFail({ - key: 'userManagement.skipInstanceOwnerSetup', - }); - expect(value).toBe('true'); - }); -}); diff --git a/packages/cli/test/integration/publicApi/executions.test.ts b/packages/cli/test/integration/publicApi/executions.test.ts index 1165069666..21ee32fc33 100644 --- a/packages/cli/test/integration/publicApi/executions.test.ts +++ b/packages/cli/test/integration/publicApi/executions.test.ts @@ -67,7 +67,6 @@ beforeEach(async () => { version: 1, }); - config.set('userManagement.disabled', false); config.set('userManagement.isInstanceOwnerSetUp', true); }); diff --git a/packages/cli/test/integration/publicApi/workflows.test.ts b/packages/cli/test/integration/publicApi/workflows.test.ts index 89b0a5a8b2..700766b9bf 100644 --- a/packages/cli/test/integration/publicApi/workflows.test.ts +++ b/packages/cli/test/integration/publicApi/workflows.test.ts @@ -63,7 +63,6 @@ beforeEach(async () => { version: 1, }); - config.set('userManagement.disabled', false); config.set('userManagement.isInstanceOwnerSetUp', true); }); diff --git a/packages/cli/test/integration/shared/constants.ts b/packages/cli/test/integration/shared/constants.ts index d259011696..a965261d16 100644 --- a/packages/cli/test/integration/shared/constants.ts +++ b/packages/cli/test/integration/shared/constants.ts @@ -44,7 +44,6 @@ export const ROUTES_REQUIRING_AUTHORIZATION: Readonly = [ 'POST /users/123/reinvite', 'GET /owner/pre-setup', 'POST /owner/setup', - 'POST /owner/skip-setup', ]; export const COMMUNITY_PACKAGE_VERSION = { diff --git a/packages/cli/test/integration/users.api.test.ts b/packages/cli/test/integration/users.api.test.ts index b600fd838c..14150eafcb 100644 --- a/packages/cli/test/integration/users.api.test.ts +++ b/packages/cli/test/integration/users.api.test.ts @@ -60,7 +60,6 @@ beforeEach(async () => { jest.mock('@/config'); - config.set('userManagement.disabled', false); config.set('userManagement.isInstanceOwnerSetUp', true); config.set('userManagement.emails.mode', 'smtp'); config.set('userManagement.emails.smtp.host', ''); @@ -379,15 +378,6 @@ describe('POST /users', () => { expect(response.body.data[0].user.inviteAcceptUrl).toBeDefined(); }); - test('should fail if user management is disabled', async () => { - config.set('userManagement.disabled', true); - config.set('userManagement.isInstanceOwnerSetUp', false); - - const response = await authOwnerAgent.post('/users').send([{ email: randomEmail() }]); - - expect(response.statusCode).toBe(400); - }); - test('should email invites and create user shells but ignore existing', async () => { const member = await testDb.createUser({ globalRole: globalMemberRole }); const memberShell = await testDb.createUserShell(globalMemberRole); diff --git a/packages/cli/test/unit/controllers/owner.controller.test.ts b/packages/cli/test/unit/controllers/owner.controller.test.ts index ad18cf2dd6..4e78a32d49 100644 --- a/packages/cli/test/unit/controllers/owner.controller.test.ts +++ b/packages/cli/test/unit/controllers/owner.controller.test.ts @@ -123,15 +123,4 @@ describe('OwnerController', () => { expect(cookieOptions.value.sameSite).toBe('lax'); }); }); - - describe('skipSetup', () => { - it('should skip setting up the instance owner', async () => { - await controller.skipSetup(); - expect(settingsRepository.update).toHaveBeenCalledWith( - { key: 'userManagement.skipInstanceOwnerSetup' }, - { value: JSON.stringify(true) }, - ); - expect(config.set).toHaveBeenCalledWith('userManagement.skipInstanceOwnerSetup', true); - }); - }); }); diff --git a/packages/cli/test/unit/middlewares/basicAuth.test.ts b/packages/cli/test/unit/middlewares/basicAuth.test.ts deleted file mode 100644 index cc03b1ec6c..0000000000 --- a/packages/cli/test/unit/middlewares/basicAuth.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import express from 'express'; -import request from 'supertest'; -import config from '@/config'; -import { setupBasicAuth } from '@/middlewares/basicAuth'; - -describe('Basic Auth Middleware', () => { - let app: express.Application; - - beforeAll(() => { - app = express(); - config.set('security.basicAuth', { user: 'jim', password: 'n8n', hash: false, active: true }); - setupBasicAuth(app, config, new RegExp('^/skip-auth')); - app.get('/test', (req, res) => res.send({ auth: true })); - app.get('/skip-auth', (req, res) => res.send({ auth: false })); - }); - - it('should not block calls to /skip-auth', async () => { - const response = await request(app).get('/skip-auth'); - expect(response.statusCode).toEqual(200); - expect(response.headers).not.toHaveProperty('www-authenticate'); - expect(response.body).toEqual({ auth: false }); - }); - - it('should block calls to /test if auth is absent', async () => { - const response = await request(app).get('/test'); - expect(response.statusCode).toEqual(401); - expect(response.headers).toHaveProperty('www-authenticate'); - }); - - it('should block calls to /test if auth is invalid', async () => { - const response = await request(app).get('/test').auth('user', 'invalid'); - expect(response.statusCode).toEqual(401); - expect(response.headers).toHaveProperty('www-authenticate'); - }); - - it('should allow access to /test if basic auth header is valid', async () => { - const response = await request(app).get('/test').auth('jim', 'n8n'); - expect(response.statusCode).toEqual(200); - expect(response.headers).not.toHaveProperty('www-authenticate'); - expect(response.body).toEqual({ auth: true }); - }); -}); diff --git a/packages/cli/test/unit/middlewares/externalJWTAuth.test.ts b/packages/cli/test/unit/middlewares/externalJWTAuth.test.ts deleted file mode 100644 index 42b2b19af4..0000000000 --- a/packages/cli/test/unit/middlewares/externalJWTAuth.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import express from 'express'; -import request from 'supertest'; -import createJWKSMock from 'mock-jwks'; -import config from '@/config'; -import { setupExternalJWTAuth } from '@/middlewares/externalJWTAuth'; - -const testJWKUri = 'https://n8n.test/'; -const jwksMock = createJWKSMock(testJWKUri); - -describe('External JWT Auth Middleware', () => { - let app: express.Application; - - beforeAll(() => { - app = express(); - config.set('security.jwtAuth.jwtHeader', 'Authorization'); - config.set('security.jwtAuth.jwtHeaderValuePrefix', 'Bearer'); - config.set('security.jwtAuth.jwtIssuer', 'n8n'); - config.set('security.jwtAuth.jwksUri', `${testJWKUri}.well-known/jwks.json`); - setupExternalJWTAuth(app, config, new RegExp('^/skip-auth')); - app.get('/test', (req, res) => res.send({ auth: true })); - app.get('/skip-auth', (req, res) => res.send({ auth: false })); - - jwksMock.start(); - }); - - it('should not block calls to /skip-auth', async () => { - const response = await request(app).get('/skip-auth'); - expect(response.statusCode).toEqual(200); - expect(response.body).toEqual({ auth: false }); - }); - - it('should block calls to /test if auth is absent', async () => - request(app).get('/test').expect(403)); - - it('should block calls to /test if auth is invalid', async () => { - const token = jwksMock.token({ iss: 'invalid' }); - const response = await request(app).get('/test').set('Authorization', `Bearer ${token}`); - expect(response.statusCode).toEqual(403); - }); - - it('should allow access to /test if JWT auth header is valid', async () => { - const token = jwksMock.token({ iss: 'n8n' }); - const response = await request(app).get('/test').set('Authorization', `Bearer ${token}`); - expect(response.statusCode).toEqual(200); - expect(response.body).toEqual({ auth: true }); - }); -}); diff --git a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue index 75a785a590..0c21776633 100644 --- a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue +++ b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue @@ -18,7 +18,7 @@ :disabled="item.disabled" :divided="item.divided" > -
+
@@ -66,6 +66,10 @@ export default defineComponent({ ElDropdownItem, N8nIcon, }, + data() { + const testIdPrefix = this.$attrs['data-test-id']; + return { testIdPrefix }; + }, props: { items: { type: Array as PropType, diff --git a/packages/editor-ui/src/App.vue b/packages/editor-ui/src/App.vue index 903aa812aa..9770fcd89c 100644 --- a/packages/editor-ui/src/App.vue +++ b/packages/editor-ui/src/App.vue @@ -142,7 +142,7 @@ export default defineComponent({ }, authenticate() { // redirect to setup page. user should be redirected to this only once - if (this.settingsStore.isUserManagementEnabled && this.settingsStore.showSetupPage) { + if (this.settingsStore.showSetupPage) { if (this.$route.name === VIEWS.SETUP) { return; } diff --git a/packages/editor-ui/src/api/users.ts b/packages/editor-ui/src/api/users.ts index bb360d4902..9e102953ab 100644 --- a/packages/editor-ui/src/api/users.ts +++ b/packages/editor-ui/src/api/users.ts @@ -38,10 +38,6 @@ export async function setupOwner( return makeRestApiRequest(context, 'POST', '/owner/setup', params as unknown as IDataObject); } -export async function skipOwnerSetup(context: IRestApiContext): Promise { - return makeRestApiRequest(context, 'POST', '/owner/skip-setup'); -} - export async function validateSignupToken( context: IRestApiContext, params: { inviterId: string; inviteeId: string }, diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index 1657e5c888..38f14b69d1 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -89,6 +89,7 @@
@@ -171,11 +172,7 @@ export default defineComponent({ return accessibleRoute !== null; }, showUserArea(): boolean { - return ( - this.settingsStore.isUserManagementEnabled && - this.usersStore.canUserAccessSidebarUserInfo && - this.usersStore.currentUser !== null - ); + return this.usersStore.canUserAccessSidebarUserInfo && this.usersStore.currentUser !== null; }, workflowExecution(): IExecutionResponse | null { return this.workflowsStore.getWorkflowExecution; diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 6deacf5843..0340765193 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -95,15 +95,11 @@ "auth.setup.createAccount": "Create account", "auth.setup.goBack": "Go back", "auth.setup.next": "Next", - "auth.setup.ownerAccountBenefits": "Setting up an owner account allows you to invite others, and prevents people using n8n without an account", "auth.setup.settingUpOwnerError": "Problem setting up owner", "auth.setup.setupConfirmation.concatEntities": "{workflows} and {credentials}", "auth.setup.setupConfirmation.credentials": "{count} credential | {count} credentials", "auth.setup.setupConfirmation.existingWorkflows": "{count} existing workflow | {count} existing workflows", "auth.setup.setupOwner": "Set up owner account", - "auth.setup.skipOwnerSetupQuestion": "Skip owner account setup?", - "auth.setup.skipSetup": "Skip setup", - "auth.setup.skipSetupTemporarily": "Skip setup for now", "auth.signin": "Sign in", "auth.signin.error": "Problem logging in", "auth.signout": "Sign out", diff --git a/packages/editor-ui/src/router.ts b/packages/editor-ui/src/router.ts index 1f00143fb2..47163f9753 100644 --- a/packages/editor-ui/src/router.ts +++ b/packages/editor-ui/src/router.ts @@ -416,12 +416,6 @@ export const routes = [ allow: { role: [ROLE.Default], }, - deny: { - shouldDeny: () => { - const settingsStore = useSettingsStore(); - return settingsStore.isUserManagementEnabled === false; - }, - }, }, }, }, @@ -537,17 +531,7 @@ export const routes = [ }, permissions: { allow: { - role: [ROLE.Default, ROLE.Owner], - }, - deny: { - shouldDeny: () => { - const settingsStore = useSettingsStore(); - - return ( - settingsStore.isUserManagementEnabled === false && - !(settingsStore.isCloudDeployment || settingsStore.isDesktopDeployment) - ); - }, + role: [ROLE.Owner], }, }, }, @@ -645,7 +629,7 @@ export const routes = [ }, permissions: { allow: { - role: [ROLE.Default, ROLE.Owner], + role: [ROLE.Owner], }, deny: { role: [ROLE.Member], @@ -665,7 +649,7 @@ export const routes = [ }, permissions: { allow: { - role: [ROLE.Default, ROLE.Owner], + role: [ROLE.Owner], }, deny: { shouldDeny: () => { @@ -707,7 +691,7 @@ export const routes = [ meta: { permissions: { allow: { - role: [ROLE.Default, ROLE.Owner], + role: [ROLE.Owner], }, deny: { role: [ROLE.Member], diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index bf8fa15b6a..7112af3cb5 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -37,7 +37,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { settings: {} as IN8nUISettings, promptsData: {} as IN8nPrompts, userManagement: { - enabled: false, showSetupOnFirstLoad: false, smtpSetup: false, authenticationMethod: UserManagementAuthenticationMethod.Email, @@ -71,9 +70,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { versionCli(): string { return this.settings.versionCli; }, - isUserManagementEnabled(): boolean { - return this.userManagement.enabled; - }, isPublicApiEnabled(): boolean { return this.api.enabled; }, diff --git a/packages/editor-ui/src/stores/users.store.ts b/packages/editor-ui/src/stores/users.store.ts index 45f2e8c819..7451efdd4d 100644 --- a/packages/editor-ui/src/stores/users.store.ts +++ b/packages/editor-ui/src/stores/users.store.ts @@ -13,7 +13,6 @@ import { sendForgotPasswordEmail, setupOwner, signup, - skipOwnerSetup, submitPersonalizationSurvey, updateCurrentUser, updateCurrentUserPassword, @@ -332,13 +331,5 @@ export const useUsersStore = defineStore(STORES.USERS, { uiStore.openModal(PERSONALIZATION_MODAL_KEY); } }, - async skipOwnerSetup(): Promise { - try { - const rootStore = useRootStore(); - const settingsStore = useSettingsStore(); - settingsStore.stopShowingSetupPage(); - await skipOwnerSetup(rootStore.getRestApiContext); - } catch (error) {} - }, }, }); diff --git a/packages/editor-ui/src/stores/webhooks.store.ts b/packages/editor-ui/src/stores/webhooks.store.ts index e333baa6db..69682244ce 100644 --- a/packages/editor-ui/src/stores/webhooks.store.ts +++ b/packages/editor-ui/src/stores/webhooks.store.ts @@ -5,7 +5,6 @@ import type { IWorkflowSettings } from 'n8n-workflow'; import { defineStore } from 'pinia'; import { useRootStore } from './n8nRoot.store'; import { useNDVStore } from './ndv.store'; -import { useSettingsStore } from './settings.store'; import { useUIStore } from './ui.store'; import { useUsersStore } from './users.store'; import { useWorkflowsStore } from './workflows.store'; @@ -21,9 +20,6 @@ export const useWebhooksStore = defineStore(STORES.WEBHOOKS, { getFakeDoorFeatures() { return useUIStore().fakeDoorFeatures; }, - isUserManagementEnabled() { - return useSettingsStore().isUserManagementEnabled; - }, getFakeDoorItems(): IFakeDoor[] { return useUIStore().fakeDoorFeatures; }, diff --git a/packages/editor-ui/src/utils/userUtils.ts b/packages/editor-ui/src/utils/userUtils.ts index b9797f0a19..a67d290353 100644 --- a/packages/editor-ui/src/utils/userUtils.ts +++ b/packages/editor-ui/src/utils/userUtils.ts @@ -101,7 +101,7 @@ export const PERMISSIONS: IUserPermissions = { TAGS: { CAN_DELETE_TAGS: { allow: { - role: [ROLE.Owner, ROLE.Default], + role: [ROLE.Owner], }, }, }, @@ -125,7 +125,7 @@ export const PERMISSIONS: IUserPermissions = { USAGE: { CAN_ACTIVATE_LICENSE: { allow: { - role: [ROLE.Owner, ROLE.Default], + role: [ROLE.Owner], }, }, }, diff --git a/packages/editor-ui/src/views/SettingsUsersView.vue b/packages/editor-ui/src/views/SettingsUsersView.vue index 64bbae3669..379d95e240 100644 --- a/packages/editor-ui/src/views/SettingsUsersView.vue +++ b/packages/editor-ui/src/views/SettingsUsersView.vue @@ -19,23 +19,7 @@
-
- -
-
+
@@ -41,7 +40,6 @@ export default defineComponent({ const FORM_CONFIG: IFormBoxConfig = { title: this.$locale.baseText('auth.setup.setupOwner'), buttonText: this.$locale.baseText('auth.setup.next'), - secondaryButtonText: this.$locale.baseText('auth.setup.skipSetupTemporarily'), inputs: [ { name: 'email', @@ -177,25 +175,6 @@ export default defineComponent({ } this.loading = false; }, - async showSkipConfirmation() { - const skip = await this.confirm( - this.$locale.baseText('auth.setup.ownerAccountBenefits'), - this.$locale.baseText('auth.setup.skipOwnerSetupQuestion'), - { - confirmButtonText: this.$locale.baseText('auth.setup.skipSetup'), - cancelButtonText: this.$locale.baseText('auth.setup.goBack'), - }, - ); - if (skip === MODAL_CONFIRM) { - this.onSkip(); - } - }, - onSkip() { - void this.usersStore.skipOwnerSetup(); - void this.$router.push({ - name: VIEWS.NEW_WORKFLOW, - }); - }, }, }); diff --git a/packages/editor-ui/src/views/SigninView.vue b/packages/editor-ui/src/views/SigninView.vue index 9aa7683875..ca03634f1d 100644 --- a/packages/editor-ui/src/views/SigninView.vue +++ b/packages/editor-ui/src/views/SigninView.vue @@ -78,7 +78,7 @@ export default defineComponent({ ], }; - if (!this.settingsStore.isDesktopDeployment || this.settingsStore.isUserManagementEnabled) { + if (!this.settingsStore.isDesktopDeployment) { this.FORM_CONFIG.redirectLink = '/forgot-password'; } }, diff --git a/packages/workflow/src/Authentication.ts b/packages/workflow/src/Authentication.ts index b743e1ecab..05fec7b4bd 100644 --- a/packages/workflow/src/Authentication.ts +++ b/packages/workflow/src/Authentication.ts @@ -1 +1 @@ -export type AuthenticationMethod = 'none' | 'email' | 'ldap' | 'saml'; +export type AuthenticationMethod = 'email' | 'ldap' | 'saml'; diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index df3216de2e..d4a056d655 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2041,7 +2041,6 @@ export interface IVersionNotificationSettings { } export interface IUserManagementSettings { - enabled: boolean; showSetupOnFirstLoad?: boolean; smtpSetup: boolean; authenticationMethod: AuthenticationMethod;