From 4e555837159850b937fba422870dd53f9e96ee5f Mon Sep 17 00:00:00 2001 From: Val <68596159+valya@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:00:14 +0000 Subject: [PATCH] feat: Add admin role to public API (no-changelog) (#7933) ## Summary Add the admin global role to the public API. This does not include porting over scopes. #### How to test the change: 1. ... ## Issues fixed Include links to Github issue or Community forum post or **Linear ticket**: > Important in order to close automatically and provide context to reviewers ... ## Review / Merge checklist - [ ] PR title and summary are descriptive. **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** ([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md)) - [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created. - [ ] Tests included. > A bug is not considered fixed, unless a test is added to prevent it from happening again. A feature is not complete without tests. > > *(internal)* You can use Slack commands to trigger [e2e tests](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#a39f9e5ba64a48b58a71d81c837e8227) or [deploy test instance](https://www.notion.so/n8n/How-to-use-Test-Instances-d65f49dfc51f441ea44367fb6f67eb0a?pvs=4#f6a177d32bde4b57ae2da0b8e454bfce) or [deploy early access version on Cloud](https://www.notion.so/n8n/Cloudbot-3dbe779836004972b7057bc989526998?pvs=4#fef2d36ab02247e1a0f65a74f6fb534e). --- .../PublicApi/v1/handlers/audit/audit.handler.ts | 2 +- .../handlers/credentials/credentials.handler.ts | 8 ++++---- .../v1/handlers/executions/executions.handler.ts | 6 +++--- .../sourceControl/sourceControl.handler.ts | 2 +- .../v1/handlers/users/users.handler.ee.ts | 4 ++-- .../v1/handlers/workflows/workflows.handler.ts | 16 ++++++++-------- .../v1/handlers/workflows/workflows.service.ts | 6 +++--- .../v1/shared/middlewares/global.middleware.ts | 3 ++- 8 files changed, 24 insertions(+), 23 deletions(-) diff --git a/packages/cli/src/PublicApi/v1/handlers/audit/audit.handler.ts b/packages/cli/src/PublicApi/v1/handlers/audit/audit.handler.ts index a6e4787134..2f9f4892b4 100644 --- a/packages/cli/src/PublicApi/v1/handlers/audit/audit.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/audit/audit.handler.ts @@ -5,7 +5,7 @@ import Container from 'typedi'; export = { generateAudit: [ - authorize(['owner']), + authorize(['owner', 'admin']), async (req: AuditRequest.Generate, res: Response): Promise => { try { const { SecurityAuditService } = await import('@/security-audit/SecurityAudit.service'); diff --git a/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.handler.ts b/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.handler.ts index fb4b8f2f38..a31656be19 100644 --- a/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.handler.ts @@ -23,7 +23,7 @@ import { Container } from 'typedi'; export = { createCredential: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), validCredentialType, validCredentialsProperties, async ( @@ -47,7 +47,7 @@ export = { }, ], deleteCredential: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async ( req: CredentialRequest.Delete, res: express.Response, @@ -55,7 +55,7 @@ export = { const { id: credentialId } = req.params; let credential: CredentialsEntity | undefined; - if (req.user.globalRole.name !== 'owner') { + if (!['owner', 'admin'].includes(req.user.globalRole.name)) { const shared = await getSharedCredentials(req.user.id, credentialId, [ 'credentials', 'role', @@ -78,7 +78,7 @@ export = { ], getCredentialType: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async (req: CredentialTypeRequest.Get, res: express.Response): Promise => { const { credentialTypeName } = req.params; diff --git a/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts b/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts index af6dd968d8..ec48fc3fa8 100644 --- a/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts @@ -12,7 +12,7 @@ import { ExecutionRepository } from '@db/repositories/execution.repository'; export = { deleteExecution: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async (req: ExecutionRequest.Delete, res: express.Response): Promise => { const sharedWorkflowsIds = await getSharedWorkflowIds(req.user); @@ -42,7 +42,7 @@ export = { }, ], getExecution: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async (req: ExecutionRequest.Get, res: express.Response): Promise => { const sharedWorkflowsIds = await getSharedWorkflowIds(req.user); @@ -71,7 +71,7 @@ export = { }, ], getExecutions: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), validCursor, async (req: ExecutionRequest.GetAll, res: express.Response): Promise => { const { diff --git a/packages/cli/src/PublicApi/v1/handlers/sourceControl/sourceControl.handler.ts b/packages/cli/src/PublicApi/v1/handlers/sourceControl/sourceControl.handler.ts index e0d06b93a8..d19741f82b 100644 --- a/packages/cli/src/PublicApi/v1/handlers/sourceControl/sourceControl.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/sourceControl/sourceControl.handler.ts @@ -14,7 +14,7 @@ import { InternalHooks } from '@/InternalHooks'; export = { pull: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin']), async ( req: PublicSourceControlRequest.Pull, res: express.Response, diff --git a/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts b/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts index 26a801e74d..f9611fa29f 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts +++ b/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts @@ -15,7 +15,7 @@ import { InternalHooks } from '@/InternalHooks'; export = { getUser: [ validLicenseWithUserQuota, - authorize(['owner']), + authorize(['owner', 'admin']), async (req: UserRequest.Get, res: express.Response) => { const { includeRole = false } = req.query; const { id } = req.params; @@ -41,7 +41,7 @@ export = { getUsers: [ validLicenseWithUserQuota, validCursor, - authorize(['owner']), + authorize(['owner', 'admin']), async (req: UserRequest.Get, res: express.Response) => { const { offset = 0, limit = 100, includeRole = false } = req.query; diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts index a6f58d44eb..c90d5ae8b3 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts @@ -31,7 +31,7 @@ import { WorkflowHistoryService } from '@/workflows/workflowHistory/workflowHist export = { createWorkflow: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async (req: WorkflowRequest.Create, res: express.Response): Promise => { const workflow = req.body; @@ -59,7 +59,7 @@ export = { }, ], deleteWorkflow: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async (req: WorkflowRequest.Get, res: express.Response): Promise => { const { id: workflowId } = req.params; @@ -74,7 +74,7 @@ export = { }, ], getWorkflow: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async (req: WorkflowRequest.Get, res: express.Response): Promise => { const { id } = req.params; @@ -95,7 +95,7 @@ export = { }, ], getWorkflows: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), validCursor, async (req: WorkflowRequest.GetAll, res: express.Response): Promise => { const { offset = 0, limit = 100, active = undefined, tags = undefined } = req.query; @@ -104,7 +104,7 @@ export = { ...(active !== undefined && { active }), }; - if (req.user.isOwner) { + if (['owner', 'admin'].includes(req.user.globalRole.name)) { if (tags) { const workflowIds = await getWorkflowIdsViaTags(parseTagNames(tags)); where.id = In(workflowIds); @@ -152,7 +152,7 @@ export = { }, ], updateWorkflow: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async (req: WorkflowRequest.Update, res: express.Response): Promise => { const { id } = req.params; const updateData = new WorkflowEntity(); @@ -214,7 +214,7 @@ export = { }, ], activateWorkflow: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async (req: WorkflowRequest.Activate, res: express.Response): Promise => { const { id } = req.params; @@ -248,7 +248,7 @@ export = { }, ], deactivateWorkflow: [ - authorize(['owner', 'member']), + authorize(['owner', 'admin', 'member']), async (req: WorkflowRequest.Activate, res: express.Response): Promise => { const { id } = req.params; diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts index 94938be25c..9ca171612e 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts @@ -18,7 +18,7 @@ function insertIf(condition: boolean, elements: string[]): string[] { } export async function getSharedWorkflowIds(user: User): Promise { - const where = user.globalRole.name === 'owner' ? {} : { userId: user.id }; + const where = ['owner', 'admin'].includes(user.globalRole.name) ? {} : { userId: user.id }; const sharedWorkflows = await Container.get(SharedWorkflowRepository).find({ where, select: ['workflowId'], @@ -32,7 +32,7 @@ export async function getSharedWorkflow( ): Promise { return Container.get(SharedWorkflowRepository).findOne({ where: { - ...(!user.isOwner && { userId: user.id }), + ...(!['owner', 'admin'].includes(user.globalRole.name) && { userId: user.id }), ...(workflowId && { workflowId }), }, relations: [...insertIf(!config.getEnv('workflowTagsDisabled'), ['workflow.tags']), 'workflow'], @@ -48,7 +48,7 @@ export async function getSharedWorkflows( ): Promise { return Container.get(SharedWorkflowRepository).find({ where: { - ...(!user.isOwner && { userId: user.id }), + ...(!['owner', 'admin'].includes(user.globalRole.name) && { userId: user.id }), ...(options.workflowIds && { workflowId: In(options.workflowIds) }), }, ...(options.relations && { relations: options.relations }), diff --git a/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts b/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts index d57aa3071b..7335ca4cd8 100644 --- a/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts +++ b/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts @@ -6,11 +6,12 @@ import { Container } from 'typedi'; import type { AuthenticatedRequest, PaginatedRequest } from '../../../types'; import { decodeCursor } from '../services/pagination.service'; import { License } from '@/License'; +import type { RoleNames } from '@/databases/entities/Role'; const UNLIMITED_USERS_QUOTA = -1; export const authorize = - (authorizedRoles: readonly string[]) => + (authorizedRoles: readonly RoleNames[]) => ( req: AuthenticatedRequest, res: express.Response,