chore(core): Use roles from database in global roles (#18768)

This commit is contained in:
Andreas Fitzek
2025-08-26 17:53:46 +02:00
committed by GitHub
parent cff3f4a67e
commit ecad12b77a
117 changed files with 956 additions and 424 deletions

View File

@@ -2,7 +2,7 @@ import type { ApiKeyWithRawValue } from '@n8n/api-types';
import { testDb, randomValidPassword, mockInstance } from '@n8n/backend-test-utils';
import { GlobalConfig } from '@n8n/config';
import type { User } from '@n8n/db';
import { ApiKeyRepository } from '@n8n/db';
import { ApiKeyRepository, GLOBAL_MEMBER_ROLE, GLOBAL_OWNER_ROLE } from '@n8n/db';
import { Container } from '@n8n/di';
import {
getApiKeyScopesForRole,
@@ -59,7 +59,7 @@ describe('Owner shell', () => {
let ownerShell: User;
beforeEach(async () => {
ownerShell = await createUserShell('global:owner');
ownerShell = await createUserShell(GLOBAL_OWNER_ROLE);
});
test('POST /api-keys should create an api key with no expiration', async () => {
@@ -304,9 +304,9 @@ describe('Owner shell', () => {
const scopes = apiKeyScopesResponse.body.data as ApiKeyScope[];
const scopesForRole = getApiKeyScopesForRole(ownerShell.role);
const scopesForRole = getApiKeyScopesForRole(ownerShell);
expect(scopes).toEqual(scopesForRole);
expect(scopes.sort()).toEqual(scopesForRole.sort());
});
});
@@ -317,7 +317,7 @@ describe('Member', () => {
beforeEach(async () => {
member = await createUser({
password: memberPassword,
role: 'global:member',
role: GLOBAL_MEMBER_ROLE,
});
await utils.setInstanceOwnerSetUp(true);
});
@@ -328,6 +328,7 @@ describe('Member', () => {
.post('/api-keys')
.send({ label: 'My API Key', expiresAt: null, scopes: ['workflow:create'] });
console.log(newApiKeyResponse.body);
expect(newApiKeyResponse.statusCode).toBe(200);
expect(newApiKeyResponse.body.data.apiKey).toBeDefined();
expect(newApiKeyResponse.body.data.apiKey).not.toBeNull();
@@ -492,8 +493,8 @@ describe('Member', () => {
const scopes = apiKeyScopesResponse.body.data as ApiKeyScope[];
const scopesForRole = getApiKeyScopesForRole(member.role);
const scopesForRole = getApiKeyScopesForRole(member);
expect(scopes).toEqual(scopesForRole);
expect(scopes.sort()).toEqual(scopesForRole.sort());
});
});

View File

@@ -1,6 +1,6 @@
import { randomValidPassword, testDb } from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { UserRepository } from '@n8n/db';
import { GLOBAL_MEMBER_ROLE, GLOBAL_OWNER_ROLE, UserRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import validator from 'validator';
@@ -36,7 +36,7 @@ describe('POST /login', () => {
beforeEach(async () => {
owner = await createUser({
password: ownerPassword,
role: 'global:owner',
role: GLOBAL_OWNER_ROLE,
});
});
@@ -140,7 +140,7 @@ describe('POST /login', () => {
license.setQuota('quota:users', 0);
const ownerUser = await createUser({
password: randomValidPassword(),
role: 'global:owner',
role: GLOBAL_OWNER_ROLE,
});
const response = await testServer.authAgentFor(ownerUser).get('/login');
@@ -182,7 +182,7 @@ describe('GET /login', () => {
});
test('should return logged-in owner shell', async () => {
const ownerShell = await createUserShell('global:owner');
const ownerShell = await createUserShell(GLOBAL_OWNER_ROLE);
const response = await testServer.authAgentFor(ownerShell).get('/login');
@@ -217,7 +217,7 @@ describe('GET /login', () => {
});
test('should return logged-in member shell', async () => {
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const response = await testServer.authAgentFor(memberShell).get('/login');
@@ -252,7 +252,7 @@ describe('GET /login', () => {
});
test('should return logged-in owner', async () => {
const owner = await createUser({ role: 'global:owner' });
const owner = await createUser({ role: GLOBAL_OWNER_ROLE });
const response = await testServer.authAgentFor(owner).get('/login');
@@ -287,7 +287,7 @@ describe('GET /login', () => {
});
test('should return logged-in member', async () => {
const member = await createUser({ role: 'global:member' });
const member = await createUser({ role: { slug: 'global:member' } });
const response = await testServer.authAgentFor(member).get('/login');
@@ -326,13 +326,13 @@ describe('GET /resolve-signup-token', () => {
beforeEach(async () => {
owner = await createUser({
password: ownerPassword,
role: 'global:owner',
role: GLOBAL_OWNER_ROLE,
});
authOwnerAgent = testServer.authAgentFor(owner);
});
test('should validate invite token', async () => {
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const response = await authOwnerAgent
.get('/resolve-signup-token')
@@ -352,7 +352,7 @@ describe('GET /resolve-signup-token', () => {
test('should return 403 if user quota reached', async () => {
license.setQuota('quota:users', 0);
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const response = await authOwnerAgent
.get('/resolve-signup-token')
@@ -363,7 +363,7 @@ describe('GET /resolve-signup-token', () => {
});
test('should fail with invalid inputs', async () => {
const { id: inviteeId } = await createUser({ role: 'global:member' });
const { id: inviteeId } = await createUser({ role: { slug: 'global:member' } });
const first = await authOwnerAgent.get('/resolve-signup-token').query({ inviterId: owner.id });
@@ -396,7 +396,7 @@ describe('GET /resolve-signup-token', () => {
describe('POST /logout', () => {
test('should log user out', async () => {
const owner = await createUser({ role: 'global:owner' });
const owner = await createUser({ role: GLOBAL_OWNER_ROLE });
const ownerAgent = testServer.authAgentFor(owner);
// @ts-expect-error `accessInfo` types are incorrect
const cookie = ownerAgent.jar.getCookie(AUTH_COOKIE_NAME, { path: '/' });

View File

@@ -40,7 +40,7 @@ describe('Auth Middleware', () => {
describe('Routes requiring Authorization', () => {
let authMemberAgent: SuperAgentTest;
beforeAll(async () => {
const member = await createUser({ role: 'global:member' });
const member = await createUser({ role: { slug: 'global:member' } });
authMemberAgent = testServer.authAgentFor(member);
});

View File

@@ -61,7 +61,7 @@ describe('--deleteWorkflowsAndCredentials', () => {
//
// ARRANGE
//
const member = await createLdapUser({ role: 'global:member' }, uuid());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uuid());
const memberProject = await getPersonalProject(member);
const workflow = await createWorkflow({}, member);
const credential = await saveCredential(randomCredentialPayload(), {
@@ -166,7 +166,7 @@ describe('--userId', () => {
//
// ARRANGE
//
const member = await createLdapUser({ role: 'global:member' }, uuid());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uuid());
await expect(command.run([`--userId=${member.id}`])).rejects.toThrowError(
`Can't migrate workflows and credentials to the user with the ID ${member.id}. That user was created via LDAP and will be deleted as well.`,
@@ -177,7 +177,7 @@ describe('--userId', () => {
//
// ARRANGE
//
const member = await createLdapUser({ role: 'global:member' }, uuid());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uuid());
const memberProject = await getPersonalProject(member);
const workflow = await createWorkflow({}, member);
const credential = await saveCredential(randomCredentialPayload(), {
@@ -242,7 +242,7 @@ describe('--projectId', () => {
//
// ARRANGE
//
const member = await createLdapUser({ role: 'global:member' }, uuid());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uuid());
const memberProject = await getPersonalProject(member);
await expect(command.run([`--projectId=${memberProject.id}`])).rejects.toThrowError(
@@ -254,7 +254,7 @@ describe('--projectId', () => {
//
// ARRANGE
//
const member = await createLdapUser({ role: 'global:member' }, uuid());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uuid());
const memberProject = await getPersonalProject(member);
const workflow = await createWorkflow({}, member);
const credential = await saveCredential(randomCredentialPayload(), {
@@ -310,7 +310,7 @@ describe('--projectId', () => {
//
// ARRANGE
//
const member = await createLdapUser({ role: 'global:member' }, uuid());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uuid());
const memberProject = await getPersonalProject(member);
const workflow = await createWorkflow({}, member);
const credential = await saveCredential(randomCredentialPayload(), {

View File

@@ -12,6 +12,7 @@ import {
SharedCredentialsRepository,
SharedWorkflowRepository,
UserRepository,
GLOBAL_OWNER_ROLE,
} from '@n8n/db';
import { Container } from '@n8n/di';
@@ -35,7 +36,7 @@ test('user-management:reset should reset DB to default user state', async () =>
//
// ARRANGE
//
const owner = await createUser({ role: 'global:owner' });
const owner = await createUser({ role: GLOBAL_OWNER_ROLE });
const ownerProject = await getPersonalProject(owner);
// should be deleted
@@ -70,7 +71,7 @@ test('user-management:reset should reset DB to default user state', async () =>
// check if the owner account was reset:
await expect(
Container.get(UserRepository).findOneBy({ role: 'global:owner' }),
Container.get(UserRepository).findOneBy({ role: { slug: GLOBAL_OWNER_ROLE.slug } }),
).resolves.toMatchObject({
email: null,
firstName: null,
@@ -80,7 +81,9 @@ test('user-management:reset should reset DB to default user state', async () =>
});
// all members were deleted:
const members = await Container.get(UserRepository).findOneBy({ role: 'global:member' });
const members = await Container.get(UserRepository).findOneBy({
role: { slug: 'global:member' },
});
expect(members).toBeNull();
// all workflows are owned by the owner:

View File

@@ -6,7 +6,12 @@ import {
randomValidPassword,
} from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { ProjectRelationRepository, UserRepository } from '@n8n/db';
import {
GLOBAL_ADMIN_ROLE,
GLOBAL_MEMBER_ROLE,
ProjectRelationRepository,
UserRepository,
} from '@n8n/db';
import { Container } from '@n8n/di';
import { Not } from '@n8n/typeorm';
@@ -52,7 +57,7 @@ describe('InvitationController', () => {
describe('POST /invitations/:id/accept', () => {
test('should fill out a member shell', async () => {
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const memberProps = {
inviterId: instanceOwner.id,
@@ -83,7 +88,7 @@ describe('InvitationController', () => {
});
test('should fill out an admin shell', async () => {
const adminShell = await createUserShell('global:admin');
const adminShell = await createUserShell(GLOBAL_ADMIN_ROLE);
const memberProps = {
inviterId: instanceOwner.id,
@@ -116,7 +121,7 @@ describe('InvitationController', () => {
test('should fail with invalid payloads', async () => {
const memberShell = await userRepository.save({
email: randomEmail(),
role: 'global:member',
role: { slug: 'global:member' },
});
const invalidPaylods = [
@@ -374,7 +379,7 @@ describe('InvitationController', () => {
mailer.invite.mockResolvedValue({ emailSent: true });
const member = await createMember();
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const newUserEmail = randomEmail();
const existingUserEmails = [member.email];

View File

@@ -9,7 +9,7 @@ import {
mockInstance,
} from '@n8n/backend-test-utils';
import type { Project, User, ListQueryDb } from '@n8n/db';
import { ProjectRepository, SharedCredentialsRepository } from '@n8n/db';
import { GLOBAL_MEMBER_ROLE, ProjectRepository, SharedCredentialsRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import type { ProjectRole } from '@n8n/permissions';
import { In } from '@n8n/typeorm';
@@ -68,10 +68,10 @@ beforeEach(async () => {
admin = await createAdmin();
ownerPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id);
member = await createUser({ role: 'global:member' });
member = await createUser({ role: { slug: 'global:member' } });
memberPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(member.id);
anotherMember = await createUser({ role: 'global:member' });
anotherMember = await createUser({ role: { slug: 'global:member' } });
anotherMemberPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
anotherMember.id,
);
@@ -110,7 +110,7 @@ describe('POST /credentials', () => {
describe('GET /credentials', () => {
test('should return all creds for owner', async () => {
const [member1, member2, member3] = await createManyUsers(3, {
role: 'global:member',
role: { slug: 'global:member' },
});
const member1PersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
member1.id,
@@ -183,7 +183,7 @@ describe('GET /credentials', () => {
test('should return only relevant creds for member', async () => {
const [member1, member2] = await createManyUsers(2, {
role: 'global:member',
role: { slug: 'global:member' },
});
const member1PersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
member1.id,
@@ -579,7 +579,7 @@ describe('GET /credentials/:id', () => {
test('should retrieve non-owned cred for owner', async () => {
const [member1, member2] = await createManyUsers(2, {
role: 'global:member',
role: { slug: 'global:member' },
});
const member1PersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
member1.id,
@@ -626,7 +626,7 @@ describe('GET /credentials/:id', () => {
test('should retrieve owned cred for member', async () => {
const [member1, member2, member3] = await createManyUsers(3, {
role: 'global:member',
role: { slug: 'global:member' },
});
const member1PersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
member1.id,
@@ -745,7 +745,7 @@ describe('PUT /credentials/:id/share', () => {
const savedCredential = await saveCredential(randomCredentialPayload(), { user: owner });
const [member1, member2, member3, member4, member5] = await createManyUsers(5, {
role: 'global:member',
role: { slug: 'global:member' },
});
// TODO: write helper for getting multiple personal projects by user id
const shareWithProjectIds = (
@@ -793,7 +793,7 @@ describe('PUT /credentials/:id/share', () => {
test('should share the credential with the provided userIds', async () => {
const [member1, member2, member3] = await createManyUsers(3, {
role: 'global:member',
role: { slug: 'global:member' },
});
const projectIds = (
await Promise.all([
@@ -876,7 +876,7 @@ describe('PUT /credentials/:id/share', () => {
test('should respond 403 for non-owned credentials for non-shared members sharing', async () => {
const savedCredential = await saveCredential(randomCredentialPayload(), { user: member });
const tempUser = await createUser({ role: 'global:member' });
const tempUser = await createUser({ role: { slug: 'global:member' } });
const tempUserPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
tempUser.id,
);
@@ -910,7 +910,7 @@ describe('PUT /credentials/:id/share', () => {
});
test('should not ignore pending sharee', async () => {
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const memberShellPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
memberShell.id,
);
@@ -1019,7 +1019,7 @@ describe('PUT /credentials/:id/share', () => {
const savedCredential = await saveCredential(randomCredentialPayload(), { user: owner });
const [member1, member2] = await createManyUsers(2, {
role: 'global:member',
role: { slug: 'global:member' },
});
await shareCredentialWithUsers(savedCredential, [member1, member2]);
@@ -1045,7 +1045,7 @@ describe('PUT /credentials/:id/share', () => {
const savedCredential = await saveCredential(randomCredentialPayload(), { user: owner });
const [member1, member2] = await createManyUsers(2, {
role: 'global:member',
role: { slug: 'global:member' },
});
await shareCredentialWithUsers(savedCredential, [member1, member2]);

View File

@@ -103,7 +103,7 @@ describe('GET /credentials', () => {
test('should return only own creds for member', async () => {
const [member1, member2] = await createManyUsers(2, {
role: 'global:member',
role: { slug: 'global:member' },
});
const [savedCredential1] = await Promise.all([
@@ -125,7 +125,7 @@ describe('GET /credentials', () => {
test('should return scopes when ?includeScopes=true', async () => {
const [member1, member2] = await createManyUsers(2, {
role: 'global:member',
role: { slug: 'global:member' },
});
const teamProject = await createTeamProject(undefined, member1);
@@ -239,7 +239,7 @@ describe('GET /credentials', () => {
test('should return data when ?includeData=true', async () => {
// ARRANGE
const [actor, otherMember] = await createManyUsers(2, {
role: 'global:member',
role: { slug: 'global:member' },
});
const teamProjectViewer = await createTeamProject(undefined);

View File

@@ -1,6 +1,6 @@
import type { SourceControlledFile } from '@n8n/api-types';
import { mockInstance } from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { GLOBAL_OWNER_ROLE, type User } from '@n8n/db';
import { Container } from '@n8n/di';
import { SourceControlPreferencesService } from '@/environments.ee/source-control/source-control-preferences.service.ee';
@@ -24,7 +24,7 @@ const testServer = utils.setupTestServer({
let sourceControlPreferencesService: SourceControlPreferencesService;
beforeAll(async () => {
owner = await createUser({ role: 'global:owner' });
owner = await createUser({ role: GLOBAL_OWNER_ROLE });
authOwnerAgent = testServer.authAgentFor(owner);
sourceControlPreferencesService = Container.get(SourceControlPreferencesService);

View File

@@ -4,6 +4,9 @@ import {
CredentialsEntity,
type Folder,
FolderRepository,
GLOBAL_ADMIN_ROLE,
GLOBAL_MEMBER_ROLE,
GLOBAL_OWNER_ROLE,
Project,
type TagEntity,
TagRepository,
@@ -217,10 +220,10 @@ describe('SourceControlService', () => {
*/
[globalAdmin, globalOwner, globalMember, projectAdmin] = await Promise.all([
await createUser({ role: 'global:admin' }),
await createUser({ role: 'global:owner' }),
await createUser({ role: 'global:member' }),
await createUser({ role: 'global:member' }),
createUser({ role: GLOBAL_ADMIN_ROLE }),
createUser({ role: GLOBAL_OWNER_ROLE }),
createUser({ role: GLOBAL_MEMBER_ROLE }),
createUser({ role: GLOBAL_MEMBER_ROLE }),
]);
[projectA, projectB] = await Promise.all([

View File

@@ -1,6 +1,11 @@
import { createWorkflow, testDb } from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { ProjectRepository, TestRunRepository } from '@n8n/db';
import {
GLOBAL_MEMBER_ROLE,
GLOBAL_OWNER_ROLE,
ProjectRepository,
TestRunRepository,
} from '@n8n/db';
import { Container } from '@n8n/di';
import { mockInstance } from 'n8n-core/test/utils';
import type { IWorkflowBase } from 'n8n-workflow';
@@ -25,7 +30,7 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
ownerShell = await createUserShell('global:owner');
ownerShell = await createUserShell(GLOBAL_OWNER_ROLE);
authOwnerAgent = testServer.authAgentFor(ownerShell);
});
@@ -113,7 +118,7 @@ describe('GET /workflows/:workflowId/test-runs', () => {
});
test('should retrieve list of test runs for a shared workflow', async () => {
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const memberAgent = testServer.authAgentFor(memberShell);
const memberPersonalProject = await Container.get(
ProjectRepository,
@@ -171,7 +176,7 @@ describe('GET /workflows/:workflowId/test-runs/:id', () => {
});
test('should retrieve test run of a shared workflow', async () => {
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const memberAgent = testServer.authAgentFor(memberShell);
const memberPersonalProject = await Container.get(
ProjectRepository,
@@ -345,7 +350,7 @@ describe('GET /workflows/:workflowId/test-runs/:id/test-cases', () => {
});
test('should return test cases for a shared workflow', async () => {
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const memberAgent = testServer.authAgentFor(memberShell);
const memberPersonalProject = await Container.get(
ProjectRepository,

View File

@@ -1,5 +1,5 @@
import { mockInstance } from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { GLOBAL_OWNER_ROLE, type User } from '@n8n/db';
import { Container } from '@n8n/di';
import axios from 'axios';
import type {
@@ -89,7 +89,7 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
owner = await createUser({ role: 'global:owner' });
owner = await createUser({ role: GLOBAL_OWNER_ROLE });
authOwnerAgent = testServer.authAgentFor(owner);
mockedSyslog.createClient.mockImplementation(() => new syslog.Client());

View File

@@ -1,5 +1,5 @@
import { mockInstance } from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { GLOBAL_OWNER_ROLE, type User } from '@n8n/db';
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
import { ExecutionRecoveryService } from '@/executions/execution-recovery.service';
@@ -25,7 +25,7 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
owner = await createUser({ role: 'global:owner' });
owner = await createUser({ role: GLOBAL_OWNER_ROLE });
authOwnerAgent = testServer.authAgentFor(owner);
});

View File

@@ -1793,7 +1793,7 @@ describe('PUT /projects/:projectId/folders/:folderId/transfer', () => {
test('should not transfer folder if license does not allow it', async () => {
testServer.license.disable('feat:folders');
const admin = await createUser({ role: 'global:admin' });
const admin = await createUser({ role: { slug: 'global:admin' } });
const sourceProject = await createTeamProject('source project', admin);
const destinationProject = await createTeamProject('destination project', member);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
@@ -1996,7 +1996,7 @@ describe('PUT /projects/:projectId/folders/:folderId/transfer', () => {
test('owner transfers folder from project they are not part of, e.g. test global cred sharing scope', async () => {
// ARRANGE
const admin = await createUser({ role: 'global:admin' });
const admin = await createUser({ role: { slug: 'global:admin' } });
const sourceProject = await createTeamProject('source project', admin);
const destinationProject = await createTeamProject('destination project', member);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });
@@ -2078,7 +2078,7 @@ describe('PUT /projects/:projectId/folders/:folderId/transfer', () => {
test('admin transfers folder from project they are not part of, e.g. test global cred sharing scope', async () => {
// ARRANGE
const admin = await createUser({ role: 'global:admin' });
const admin = await createUser({ role: { slug: 'global:admin' } });
const sourceProject = await createTeamProject('source project', owner);
const destinationProject = await createTeamProject('destination project', owner);
const sourceFolder1 = await createFolder(sourceProject, { name: 'Source Folder 1' });

View File

@@ -8,6 +8,7 @@ import { createCompactedInsightsEvent } from '@/modules/insights/database/entiti
import { createUser } from '../shared/db/users';
import type { SuperAgentTest } from '../shared/types';
import * as utils from '../shared/utils';
import { GLOBAL_ADMIN_ROLE, GLOBAL_MEMBER_ROLE, GLOBAL_OWNER_ROLE } from '@n8n/db';
mockInstance(Telemetry);
@@ -20,9 +21,9 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
const owner = await createUser({ role: 'global:owner' });
const admin = await createUser({ role: 'global:admin' });
const member = await createUser({ role: 'global:member' });
const owner = await createUser({ role: GLOBAL_OWNER_ROLE });
const admin = await createUser({ role: GLOBAL_ADMIN_ROLE });
const member = await createUser({ role: GLOBAL_MEMBER_ROLE });
agents.owner = testServer.authAgentFor(owner);
agents.admin = testServer.authAgentFor(admin);
agents.member = testServer.authAgentFor(member);

View File

@@ -7,7 +7,12 @@ import {
} from '@n8n/backend-test-utils';
import { LDAP_DEFAULT_CONFIGURATION } from '@n8n/constants';
import type { User } from '@n8n/db';
import { AuthProviderSyncHistoryRepository, UserRepository } from '@n8n/db';
import {
AuthProviderSyncHistoryRepository,
GLOBAL_MEMBER_ROLE,
GLOBAL_OWNER_ROLE,
UserRepository,
} from '@n8n/db';
import { Container } from '@n8n/di';
import { Not } from '@n8n/typeorm';
import type { Entry as LdapUser } from 'ldapts';
@@ -37,7 +42,7 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
owner = await createUser({ role: 'global:owner' });
owner = await createUser({ role: GLOBAL_OWNER_ROLE });
authOwnerAgent = testServer.authAgentFor(owner);
defaultLdapConfig.bindingAdminPassword = Container.get(Cipher).encrypt(
@@ -65,7 +70,7 @@ beforeEach(async () => {
});
test('Member role should not be able to access ldap routes', async () => {
const member = await createUser({ role: 'global:member' });
const member = await createUser({ role: { slug: 'global:member' } });
const authAgent = testServer.authAgentFor(member);
await authAgent.get('/ldap/config').expect(403);
await authAgent.put('/ldap/config').expect(403);
@@ -137,7 +142,7 @@ describe('PUT /ldap/config', () => {
const ldapConfig = await createLdapConfig();
Container.get(LdapService).setConfig(ldapConfig);
const member = await createLdapUser({ role: 'global:member' }, uniqueId());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uniqueId());
const configuration = ldapConfig;
@@ -250,7 +255,7 @@ describe('POST /ldap/sync', () => {
const ldapUserId = uniqueId();
const member = await createLdapUser(
{ role: 'global:member', email: ldapUserEmail },
{ role: { slug: 'global:member' }, email: ldapUserEmail },
ldapUserId,
);
@@ -279,7 +284,7 @@ describe('POST /ldap/sync', () => {
const ldapUserId = uniqueId();
const member = await createLdapUser(
{ role: 'global:member', email: ldapUserEmail },
{ role: { slug: 'global:member' }, email: ldapUserEmail },
ldapUserId,
);
@@ -364,7 +369,7 @@ describe('POST /ldap/sync', () => {
await createLdapUser(
{
role: 'global:member',
role: { slug: 'global:member' },
email: ldapUser.mail,
firstName: ldapUser.givenName,
lastName: randomName(),
@@ -397,7 +402,7 @@ describe('POST /ldap/sync', () => {
await createLdapUser(
{
role: 'global:member',
role: { slug: 'global:member' },
email: ldapUser.mail,
firstName: ldapUser.givenName,
lastName: ldapUser.sn,
@@ -426,7 +431,7 @@ describe('POST /ldap/sync', () => {
});
test('should remove user instance access once the user is disabled during synchronization', async () => {
const member = await createLdapUser({ role: 'global:member' }, uniqueId());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uniqueId());
jest.spyOn(LdapService.prototype, 'searchWithAdminBinding').mockResolvedValue([]);
@@ -485,7 +490,7 @@ describe('POST /ldap/sync', () => {
// Create user with valid email first
await createLdapUser(
{
role: 'global:member',
role: GLOBAL_MEMBER_ROLE,
email: originalEmail,
firstName: randomName(),
lastName: randomName(),
@@ -603,7 +608,7 @@ describe('POST /login', () => {
await createLdapUser(
{
role: 'global:member',
role: { slug: 'global:member' },
email: ldapUser.mail,
firstName: 'firstname',
lastName: 'lastname',
@@ -637,7 +642,7 @@ describe('POST /login', () => {
};
await createUser({
role: 'global:member',
role: GLOBAL_MEMBER_ROLE,
email: ldapUser.mail,
firstName: ldapUser.givenName,
lastName: 'lastname',
@@ -652,7 +657,7 @@ describe('Instance owner should able to delete LDAP users', () => {
const ldapConfig = await createLdapConfig();
Container.get(LdapService).setConfig(ldapConfig);
const member = await createLdapUser({ role: 'global:member' }, uniqueId());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uniqueId());
await authOwnerAgent.post(`/users/${member.id}`);
});
@@ -661,7 +666,7 @@ describe('Instance owner should able to delete LDAP users', () => {
const ldapConfig = await createLdapConfig();
Container.get(LdapService).setConfig(ldapConfig);
const member = await createLdapUser({ role: 'global:member' }, uniqueId());
const member = await createLdapUser({ role: { slug: 'global:member' } }, uniqueId());
// delete the LDAP member and transfer its workflows/credentials to instance owner
await authOwnerAgent.post(`/users/${member.id}?transferId=${owner.id}`);

View File

@@ -1,5 +1,5 @@
import { testDb } from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { GLOBAL_MEMBER_ROLE, GLOBAL_OWNER_ROLE, type User } from '@n8n/db';
import nock from 'nock';
import config from '@/config';
@@ -21,8 +21,8 @@ let authMemberAgent: SuperAgentTest;
const testServer = utils.setupTestServer({ endpointGroups: ['license'] });
beforeAll(async () => {
owner = await createUserShell('global:owner');
member = await createUserShell('global:member');
owner = await createUserShell(GLOBAL_OWNER_ROLE);
member = await createUserShell(GLOBAL_MEMBER_ROLE);
authOwnerAgent = testServer.authAgentFor(owner);
authMemberAgent = testServer.authAgentFor(member);

View File

@@ -7,7 +7,7 @@ import {
} from '@n8n/backend-test-utils';
import { GlobalConfig } from '@n8n/config';
import type { User } from '@n8n/db';
import { ProjectRepository, UserRepository } from '@n8n/db';
import { GLOBAL_OWNER_ROLE, ProjectRepository, UserRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import type { IPersonalizationSurveyAnswersV4 } from 'n8n-workflow';
import validator from 'validator';
@@ -32,7 +32,7 @@ describe('Owner shell', () => {
let authOwnerShellAgent: SuperAgentTest;
beforeEach(async () => {
ownerShell = await createUserShell('global:owner');
ownerShell = await createUserShell(GLOBAL_OWNER_ROLE);
authOwnerShellAgent = testServer.authAgentFor(ownerShell);
});
@@ -139,7 +139,7 @@ describe('Member', () => {
beforeEach(async () => {
member = await createUser({
password: memberPassword,
role: 'global:member',
role: { slug: 'global:member' },
});
authMemberAgent = testServer.authAgentFor(member);
await utils.setInstanceOwnerSetUp(true);
@@ -243,7 +243,7 @@ describe('Member', () => {
describe('Owner', () => {
test('PATCH /me should succeed with valid inputs', async () => {
const owner = await createUser({ role: 'global:owner' });
const owner = await createUser({ role: GLOBAL_OWNER_ROLE });
const authOwnerAgent = testServer.authAgentFor(owner);
for (const validPayload of VALID_PATCH_ME_PAYLOADS) {

View File

@@ -30,6 +30,11 @@ beforeEach(async () => {
owner = await createOwner();
owner = await Container.get(UserRepository).findOneOrFail({
where: { id: owner.id },
relations: ['role'],
});
externalHooks.run.mockReset();
config.set('userManagement.disabled', false);

View File

@@ -6,7 +6,7 @@ import {
testDb,
} from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { UserRepository } from '@n8n/db';
import { GLOBAL_OWNER_ROLE, UserRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import validator from 'validator';
@@ -20,7 +20,7 @@ const testServer = utils.setupTestServer({ endpointGroups: ['owner'] });
let ownerShell: User;
beforeEach(async () => {
ownerShell = await createUserShell('global:owner');
ownerShell = await createUserShell(GLOBAL_OWNER_ROLE);
config.set('userManagement.isInstanceOwnerSetUp', false);
});

View File

@@ -7,7 +7,7 @@ import {
mockInstance,
} from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { UserRepository } from '@n8n/db';
import { GLOBAL_MEMBER_ROLE, GLOBAL_OWNER_ROLE, UserRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import { compare } from 'bcryptjs';
import { mock } from 'jest-mock-extended';
@@ -39,8 +39,8 @@ let authService: AuthService;
beforeEach(async () => {
await testDb.truncate(['User']);
owner = await createUser({ role: 'global:owner' });
member = await createUser({ role: 'global:member' });
owner = await createUser({ role: GLOBAL_OWNER_ROLE });
member = await createUser({ role: GLOBAL_MEMBER_ROLE });
externalHooks.run.mockReset();
jest.replaceProperty(mailer, 'isEmailSetUp', true);
authService = Container.get(AuthService);
@@ -50,7 +50,7 @@ describe('POST /forgot-password', () => {
test('should send password reset email', async () => {
const member = await createUser({
email: 'test@test.com',
role: 'global:member',
role: { slug: 'global:member' },
});
await Promise.all(
@@ -76,7 +76,7 @@ describe('POST /forgot-password', () => {
await setCurrentAuthenticationMethod('saml');
const member = await createUser({
email: 'test@test.com',
role: 'global:member',
role: { slug: 'global:member' },
});
await testServer.authlessAgent

View File

@@ -222,7 +222,7 @@ describe('Public API endpoints with feat:apiKeyScopes enabled', () => {
expect(returnedUser.id).toBe(storedUser.id);
expect(returnedUser.email).toBe(storedUser.email);
expect(returnedUser.email).toBe(payloadUser.email);
expect(storedUser.role).toBe(payloadUser.role);
expect(storedUser.role.slug).toBe(payloadUser.role);
});
test('should fail to create user when API key doesn\'t have "user:create" scope', async () => {
@@ -267,7 +267,7 @@ describe('Public API endpoints with feat:apiKeyScopes enabled', () => {
*/
expect(response.status).toBe(204);
const storedUser = await getUserById(member.id);
expect(storedUser.role).toBe(payload.newRoleName);
expect(storedUser.role.slug).toBe(payload.newRoleName);
});
test('should fail to change role when API key doesn\'t have "user:changeRole" scope', async () => {

View File

@@ -4,7 +4,7 @@ import {
testDb,
mockInstance,
} from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { GLOBAL_MEMBER_ROLE, type User } from '@n8n/db';
import { v4 as uuid } from 'uuid';
import validator from 'validator';
@@ -155,7 +155,7 @@ describe('With license unlimited quota:users', () => {
test('should return a pending user', async () => {
const owner = await createOwnerWithApiKey();
const { id: memberId } = await createUserShell('global:member');
const { id: memberId } = await createUserShell(GLOBAL_MEMBER_ROLE);
const authOwnerAgent = testServer.publicApiAgentFor(owner);
const response = await authOwnerAgent.get(`/users/${memberId}`).expect(200);

View File

@@ -95,7 +95,7 @@ describe('Users in Public API', () => {
expect(returnedUser.id).toBe(storedUser.id);
expect(returnedUser.email).toBe(storedUser.email);
expect(returnedUser.email).toBe(payloadUser.email);
expect(storedUser.role).toBe(payloadUser.role);
expect(storedUser.role.slug).toBe(payloadUser.role);
});
});
@@ -275,7 +275,7 @@ describe('Users in Public API', () => {
*/
expect(response.status).toBe(204);
const storedUser = await getUserById(member.id);
expect(storedUser.role).toBe(payload.newRoleName);
expect(storedUser.role.slug).toBe(payload.newRoleName);
});
});
});

View File

@@ -1,14 +1,23 @@
import { randomEmail, randomName, randomValidPassword } from '@n8n/backend-test-utils';
import { AuthIdentity, AuthIdentityRepository, UserRepository } from '@n8n/db';
import {
AuthIdentity,
AuthIdentityRepository,
GLOBAL_ADMIN_ROLE,
GLOBAL_MEMBER_ROLE,
GLOBAL_OWNER_ROLE,
type Role,
UserRepository,
} from '@n8n/db';
import { type User } from '@n8n/db';
import { Container } from '@n8n/di';
import type { ApiKeyScope, GlobalRole } from '@n8n/permissions';
import type { ApiKeyScope } from '@n8n/permissions';
import { getApiKeyScopesForRole } from '@n8n/permissions';
import { hash } from 'bcryptjs';
import { MfaService } from '@/mfa/mfa.service';
import { TOTPService } from '@/mfa/totp.service';
import { PublicApiKeyService } from '@/services/public-api-key.service';
import type { DeepPartial } from '@n8n/typeorm';
type ApiKeyOptions = {
expiresAt?: number | null;
@@ -32,26 +41,26 @@ async function handlePasswordSetup(password: string | null | undefined): Promise
}
/** Store a new user object, defaulting to a `member` */
export async function newUser(attributes: Partial<User> = {}): Promise<User> {
export async function newUser(attributes: DeepPartial<User> = {}): Promise<User> {
const { email, password, firstName, lastName, role, ...rest } = attributes;
return Container.get(UserRepository).create({
email: email ?? randomEmail(),
password: await handlePasswordSetup(password),
firstName: firstName ?? randomName(),
lastName: lastName ?? randomName(),
role: role ?? 'global:member',
role: role ?? GLOBAL_MEMBER_ROLE,
...rest,
});
}
/** Store a user object in the DB */
export async function createUser(attributes: Partial<User> = {}): Promise<User> {
export async function createUser(attributes: DeepPartial<User> = {}): Promise<User> {
const userInstance = await newUser(attributes);
const { user } = await Container.get(UserRepository).createUserWithProject(userInstance);
return user;
}
export async function createLdapUser(attributes: Partial<User>, ldapId: string): Promise<User> {
export async function createLdapUser(attributes: DeepPartial<User>, ldapId: string): Promise<User> {
const user = await createUser(attributes);
await Container.get(AuthIdentityRepository).save(AuthIdentity.create(user, ldapId, 'ldap'));
return user;
@@ -105,7 +114,7 @@ export const addApiKey = async (
return await Container.get(PublicApiKeyService).createPublicApiKeyForUser(user, {
label: randomName(),
expiresAt,
scopes: scopes.length ? scopes : getApiKeyScopesForRole(user.role),
scopes: scopes.length ? scopes : getApiKeyScopesForRole(user),
});
};
@@ -134,21 +143,21 @@ export async function createAdminWithApiKey({ expiresAt = null, scopes = [] }: A
}
export async function createOwner() {
return await createUser({ role: 'global:owner' });
return await createUser({ role: GLOBAL_OWNER_ROLE });
}
export async function createMember() {
return await createUser({ role: 'global:member' });
return await createUser({ role: GLOBAL_MEMBER_ROLE });
}
export async function createAdmin() {
return await createUser({ role: 'global:admin' });
return await createUser({ role: GLOBAL_ADMIN_ROLE });
}
export async function createUserShell(role: GlobalRole): Promise<User> {
const shell: Partial<User> = { role };
export async function createUserShell(role: Role): Promise<User> {
const shell: DeepPartial<User> = { role };
if (role !== 'global:owner') {
if (role.slug !== GLOBAL_OWNER_ROLE.slug) {
shell.email = randomEmail();
}
@@ -161,7 +170,7 @@ export async function createUserShell(role: GlobalRole): Promise<User> {
*/
export async function createManyUsers(
amount: number,
attributes: Partial<User> = {},
attributes: DeepPartial<User> = {},
): Promise<User[]> {
const result = await Promise.all(
Array(amount)
@@ -176,13 +185,13 @@ export async function createManyUsers(
export const getAllUsers = async () =>
await Container.get(UserRepository).find({
relations: ['authIdentities'],
relations: ['authIdentities', 'role'],
});
export const getUserById = async (id: string) =>
await Container.get(UserRepository).findOneOrFail({
where: { id },
relations: ['authIdentities'],
relations: ['authIdentities', 'role'],
});
export const getLdapIdentities = async () =>
@@ -192,5 +201,8 @@ export const getLdapIdentities = async () =>
});
export async function getGlobalOwner() {
return await Container.get(UserRepository).findOneByOrFail({ role: 'global:owner' });
return await Container.get(UserRepository).findOneOrFail({
where: { role: { slug: GLOBAL_OWNER_ROLE.slug } },
relations: ['role'],
});
}

View File

@@ -1,5 +1,5 @@
import { testDb } from '@n8n/backend-test-utils';
import { TagRepository } from '@n8n/db';
import { GLOBAL_OWNER_ROLE, TagRepository } from '@n8n/db';
import { Container } from '@n8n/di';
import { createUserShell } from './shared/db/users';
@@ -11,7 +11,7 @@ let authOwnerAgent: SuperAgentTest;
const testServer = utils.setupTestServer({ endpointGroups: ['tags'] });
beforeAll(async () => {
const ownerShell = await createUserShell('global:owner');
const ownerShell = await createUserShell(GLOBAL_OWNER_ROLE);
authOwnerAgent = testServer.authAgentFor(ownerShell);
});

View File

@@ -44,7 +44,7 @@ describe('UserRepository', () => {
test('should create personal project for a user', async () => {
const { user, project } = await userRepository.createUserWithProject({
email: randomEmail(),
role: 'global:member',
role: { slug: 'global:member' },
});
const projectRelation = await Container.get(ProjectRelationRepository).findOneOrFail({

View File

@@ -12,6 +12,9 @@ import {
import type { PublicUser, User } from '@n8n/db';
import {
FolderRepository,
GLOBAL_ADMIN_ROLE,
GLOBAL_MEMBER_ROLE,
GLOBAL_OWNER_ROLE,
ProjectRelationRepository,
ProjectRepository,
SharedCredentialsRepository,
@@ -61,26 +64,26 @@ describe('GET /users', () => {
userRepository = Container.get(UserRepository);
owner = await createUser({
role: 'global:owner',
role: GLOBAL_OWNER_ROLE,
email: 'owner@n8n.io',
firstName: 'OwnerFirstName',
lastName: 'OwnerLastName',
});
member1 = await createUser({
role: 'global:member',
role: GLOBAL_MEMBER_ROLE,
email: 'member1@n8n.io',
firstName: 'Member1FirstName',
lastName: 'Member1LastName',
mfaEnabled: true,
});
member2 = await createUser({
role: 'global:member',
role: GLOBAL_MEMBER_ROLE,
email: 'member2@n8n.io',
firstName: 'Member2FirstName',
lastName: 'Member2LastName',
});
await createUser({
role: 'global:admin',
role: GLOBAL_ADMIN_ROLE,
email: 'admin@n8n.io',
firstName: 'AdminFirstName',
lastName: 'AdminLastName',
@@ -575,7 +578,7 @@ describe('GET /users', () => {
let pendingUser: User;
beforeAll(async () => {
pendingUser = await createUser({
role: 'global:member',
role: { slug: 'global:member' },
email: 'pending@n8n.io',
firstName: 'PendingFirstName',
lastName: 'PendingLastName',
@@ -723,14 +726,14 @@ describe('GET /users', () => {
test('should sort by firstName and lastName combined', async () => {
const user1 = await createUser({
role: 'global:member',
role: { slug: 'global:member' },
email: 'memberz1@n8n.io',
firstName: 'ZZZFirstName',
lastName: 'ZZZLastName',
});
const user2 = await createUser({
role: 'global:member',
role: { slug: 'global:member' },
email: 'memberz2@n8n.io',
firstName: 'ZZZFirstName',
lastName: 'ZZYLastName',
@@ -1423,7 +1426,7 @@ describe('PATCH /users/:id/role', () => {
const user = await getUserById(otherAdmin.id);
expect(user.role).toBe('global:member');
expect(user.role.slug).toBe('global:member');
// restore other admin
@@ -1441,7 +1444,7 @@ describe('PATCH /users/:id/role', () => {
const user = await getUserById(admin.id);
expect(user.role).toBe('global:member');
expect(user.role.slug).toBe('global:member');
// restore admin
@@ -1457,9 +1460,9 @@ describe('PATCH /users/:id/role', () => {
expect(response.statusCode).toBe(200);
expect(response.body.data).toStrictEqual({ success: true });
const user = await getUserById(admin.id);
const user = await getUserById(member.id);
expect(user.role).toBe('global:admin');
expect(user.role.slug).toBe('global:admin');
// restore member
@@ -1508,7 +1511,7 @@ describe('PATCH /users/:id/role', () => {
const user = await getUserById(admin.id);
expect(user.role).toBe('global:admin');
expect(user.role.slug).toBe('global:admin');
// restore member
@@ -1526,7 +1529,7 @@ describe('PATCH /users/:id/role', () => {
const user = await getUserById(admin.id);
expect(user.role).toBe('global:member');
expect(user.role.slug).toBe('global:member');
// restore admin

View File

@@ -1,6 +1,6 @@
import { LicenseState } from '@n8n/backend-common';
import { createWorkflow, shareWorkflowWithUsers, testDb } from '@n8n/backend-test-utils';
import type { User } from '@n8n/db';
import { GLOBAL_MEMBER_ROLE, GLOBAL_OWNER_ROLE, type User } from '@n8n/db';
import { Container } from '@n8n/di';
import { mock } from 'jest-mock-extended';
@@ -17,9 +17,9 @@ let projectService: ProjectService;
beforeAll(async () => {
await testDb.init();
owner = await createUser({ role: 'global:owner' });
member = await createUser({ role: 'global:member' });
anotherMember = await createUser({ role: 'global:member' });
owner = await createUser({ role: GLOBAL_OWNER_ROLE });
member = await createUser({ role: GLOBAL_MEMBER_ROLE });
anotherMember = await createUser({ role: GLOBAL_MEMBER_ROLE });
const licenseMock = mock<LicenseState>();
licenseMock.isSharingLicensed.mockReturnValue(true);
licenseMock.getMaxTeamProjects.mockReturnValue(-1);
@@ -39,7 +39,6 @@ afterAll(async () => {
describe('WorkflowSharingService', () => {
describe('getSharedWorkflowIds', () => {
it('should show all workflows to owners', async () => {
owner.role = 'global:owner';
const workflow1 = await createWorkflow({}, member);
const workflow2 = await createWorkflow({}, anotherMember);
const sharedWorkflowIds = await workflowSharingService.getSharedWorkflowIds(owner, {
@@ -51,7 +50,6 @@ describe('WorkflowSharingService', () => {
});
it('should show shared workflows to users', async () => {
member.role = 'global:member';
const workflow1 = await createWorkflow({}, anotherMember);
const workflow2 = await createWorkflow({}, anotherMember);
const workflow3 = await createWorkflow({}, anotherMember);

View File

@@ -21,7 +21,7 @@ const testServer = utils.setupTestServer({
});
beforeAll(async () => {
member = await createUser({ role: 'global:member' });
member = await createUser({ role: { slug: 'global:member' } });
await utils.initNodeTypes();
});

View File

@@ -16,6 +16,7 @@ import {
WorkflowHistoryRepository,
SharedWorkflowRepository,
WorkflowRepository,
GLOBAL_MEMBER_ROLE,
} from '@n8n/db';
import { Container } from '@n8n/di';
import type { ProjectRole } from '@n8n/permissions';
@@ -70,9 +71,9 @@ beforeAll(async () => {
owner = await createOwner();
admin = await createAdmin();
ownerPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(owner.id);
member = await createUser({ role: 'global:member' });
member = await createUser({ role: { slug: 'global:member' } });
memberPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(member.id);
anotherMember = await createUser({ role: 'global:member' });
anotherMember = await createUser({ role: { slug: 'global:member' } });
anotherMemberPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
anotherMember.id,
);
@@ -159,7 +160,7 @@ describe('PUT /workflows/:workflowId/share', () => {
test('should allow sharing with pending users', async () => {
const workflow = await createWorkflow({}, owner);
const memberShell = await createUserShell('global:member');
const memberShell = await createUserShell(GLOBAL_MEMBER_ROLE);
const memberShellPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
memberShell.id,
);
@@ -272,7 +273,7 @@ describe('PUT /workflows/:workflowId/share', () => {
test('should not allow sharing by another non-shared member', async () => {
const workflow = await createWorkflow({}, member);
const tempUser = await createUser({ role: 'global:member' });
const tempUser = await createUser({ role: { slug: 'global:member' } });
const tempUserPersonalProject = await projectRepository.getPersonalProjectForUserOrFail(
tempUser.id,
);

View File

@@ -657,7 +657,7 @@ describe('GET /workflows', () => {
test('should return workflows with scopes when ?includeScopes=true', async () => {
const [member1, member2] = await createManyUsers(2, {
role: 'global:member',
role: { slug: 'global:member' },
});
const teamProject = await createTeamProject(undefined, member1);
@@ -1474,7 +1474,7 @@ describe('GET /workflows?includeFolders=true', () => {
test('should return workflows with scopes and folders when ?includeScopes=true', async () => {
const [member1, member2] = await createManyUsers(2, {
role: 'global:member',
role: { slug: 'global:member' },
});
const teamProject = await createTeamProject(undefined, member1);
@@ -2165,6 +2165,8 @@ describe('PATCH /workflows/:workflowId', () => {
const response = await authOwnerAgent.patch(`/workflows/${workflow.id}`).send(payload);
console.log(response.body);
const {
data: { id },
} = response.body;