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).
This commit is contained in:
Val 2023-12-05 15:00:14 +00:00 committed by GitHub
parent 50e416d9ae
commit 4e55583715
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 24 additions and 23 deletions

View file

@ -5,7 +5,7 @@ import Container from 'typedi';
export = {
generateAudit: [
authorize(['owner']),
authorize(['owner', 'admin']),
async (req: AuditRequest.Generate, res: Response): Promise<Response> => {
try {
const { SecurityAuditService } = await import('@/security-audit/SecurityAudit.service');

View file

@ -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<express.Response> => {
const { credentialTypeName } = req.params;

View file

@ -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<express.Response> => {
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<express.Response> => {
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<express.Response> => {
const {

View file

@ -14,7 +14,7 @@ import { InternalHooks } from '@/InternalHooks';
export = {
pull: [
authorize(['owner', 'member']),
authorize(['owner', 'admin']),
async (
req: PublicSourceControlRequest.Pull,
res: express.Response,

View file

@ -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;

View file

@ -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<express.Response> => {
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<express.Response> => {
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<express.Response> => {
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<express.Response> => {
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<express.Response> => {
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<express.Response> => {
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<express.Response> => {
const { id } = req.params;

View file

@ -18,7 +18,7 @@ function insertIf(condition: boolean, elements: string[]): string[] {
}
export async function getSharedWorkflowIds(user: User): Promise<string[]> {
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<SharedWorkflow | null> {
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<SharedWorkflow[]> {
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 }),

View file

@ -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,