mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
feat(API): Add user management endpoints to the Projects Public API (#12329)
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in> Co-authored-by: Danny Martini <danny@n8n.io> Co-authored-by: Andreas Fitzek <andreas.fitzek@n8n.io> Co-authored-by: Guillaume Jacquart <jacquart.guillaume@gmail.com>
This commit is contained in:
@@ -28,6 +28,8 @@ export { ChangePasswordRequestDto } from './password-reset/change-password-reque
|
||||
export { CreateProjectDto } from './project/create-project.dto';
|
||||
export { UpdateProjectDto } from './project/update-project.dto';
|
||||
export { DeleteProjectDto } from './project/delete-project.dto';
|
||||
export { AddUsersToProjectDto } from './project/add-users-to-project.dto';
|
||||
export { ChangeUserRoleInProject } from './project/change-user-role-in-project.dto';
|
||||
|
||||
export { SamlAcsDto } from './saml/saml-acs.dto';
|
||||
export { SamlPreferences } from './saml/saml-preferences.dto';
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import { AddUsersToProjectDto } from '../add-users-to-project.dto';
|
||||
|
||||
describe('AddUsersToProjectDto', () => {
|
||||
describe('Valid requests', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'with single user',
|
||||
request: {
|
||||
relations: [
|
||||
{
|
||||
userId: 'user-123',
|
||||
role: 'project:admin',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'with multiple relations',
|
||||
request: {
|
||||
relations: [
|
||||
{
|
||||
userId: 'user-123',
|
||||
role: 'project:admin',
|
||||
},
|
||||
{
|
||||
userId: 'user-456',
|
||||
role: 'project:editor',
|
||||
},
|
||||
{
|
||||
userId: 'user-789',
|
||||
role: 'project:viewer',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'with all possible roles unless the `project:personalOwner`',
|
||||
request: {
|
||||
relations: [
|
||||
{ userId: 'user-1', role: 'project:admin' },
|
||||
{ userId: 'user-2', role: 'project:editor' },
|
||||
{ userId: 'user-3', role: 'project:viewer' },
|
||||
],
|
||||
},
|
||||
},
|
||||
])('should validate $name', ({ request }) => {
|
||||
const result = AddUsersToProjectDto.safeParse(request);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invalid requests', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'missing relations array',
|
||||
request: {},
|
||||
expectedErrorPath: ['relations'],
|
||||
},
|
||||
{
|
||||
name: 'empty relations array',
|
||||
request: {
|
||||
relations: [],
|
||||
},
|
||||
expectedErrorPath: ['relations'],
|
||||
},
|
||||
{
|
||||
name: 'invalid userId type',
|
||||
request: {
|
||||
relations: [
|
||||
{
|
||||
userId: 123,
|
||||
role: 'project:admin',
|
||||
},
|
||||
],
|
||||
},
|
||||
expectedErrorPath: ['relations', 0, 'userId'],
|
||||
},
|
||||
{
|
||||
name: 'empty userId',
|
||||
request: {
|
||||
relations: [
|
||||
{
|
||||
userId: '',
|
||||
role: 'project:admin',
|
||||
},
|
||||
],
|
||||
},
|
||||
expectedErrorPath: ['relations', 0, 'userId'],
|
||||
},
|
||||
{
|
||||
name: 'invalid role',
|
||||
request: {
|
||||
relations: [
|
||||
{
|
||||
userId: 'user-123',
|
||||
role: 'invalid-role',
|
||||
},
|
||||
],
|
||||
},
|
||||
expectedErrorPath: ['relations', 0, 'role'],
|
||||
},
|
||||
{
|
||||
name: 'missing role',
|
||||
request: {
|
||||
relations: [
|
||||
{
|
||||
userId: 'user-123',
|
||||
},
|
||||
],
|
||||
},
|
||||
expectedErrorPath: ['relations', 0, 'role'],
|
||||
},
|
||||
{
|
||||
name: 'invalid relations array type',
|
||||
request: {
|
||||
relations: 'not-an-array',
|
||||
},
|
||||
expectedErrorPath: ['relations'],
|
||||
},
|
||||
{
|
||||
name: 'invalid user object in array',
|
||||
request: {
|
||||
relations: ['not-an-object'],
|
||||
},
|
||||
expectedErrorPath: ['relations', 0],
|
||||
},
|
||||
{
|
||||
name: 'invalid with `project:personalOwner` role',
|
||||
request: {
|
||||
relations: [{ userId: 'user-1', role: 'project:personalOwner' }],
|
||||
},
|
||||
},
|
||||
])('should fail validation for $name', ({ request, expectedErrorPath }) => {
|
||||
const result = AddUsersToProjectDto.safeParse(request);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
|
||||
if (expectedErrorPath) {
|
||||
expect(result.error?.issues[0].path).toEqual(expectedErrorPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ChangeUserRoleInProject } from '../change-user-role-in-project.dto';
|
||||
|
||||
describe('ChangeUserRoleInProject', () => {
|
||||
describe('Allow valid roles', () => {
|
||||
test.each(['project:admin', 'project:editor', 'project:viewer'])('should allow %s', (role) => {
|
||||
const result = ChangeUserRoleInProject.safeParse({ role });
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Reject invalid roles', () => {
|
||||
test.each([
|
||||
{
|
||||
name: 'missing role',
|
||||
request: {},
|
||||
expectedErrorPath: ['role'],
|
||||
},
|
||||
{
|
||||
name: 'empty role',
|
||||
request: {
|
||||
role: '',
|
||||
},
|
||||
expectedErrorPath: ['role'],
|
||||
},
|
||||
{
|
||||
name: 'invalid role type',
|
||||
request: {
|
||||
role: 123,
|
||||
},
|
||||
expectedErrorPath: ['role'],
|
||||
},
|
||||
{
|
||||
name: 'invalid role value',
|
||||
request: {
|
||||
role: 'invalid-role',
|
||||
},
|
||||
expectedErrorPath: ['role'],
|
||||
},
|
||||
{
|
||||
name: 'personal owner role',
|
||||
request: { role: 'project:personalOwner' },
|
||||
expectedErrorPath: ['role'],
|
||||
},
|
||||
])('should reject $name', ({ request, expectedErrorPath }) => {
|
||||
const result = ChangeUserRoleInProject.safeParse(request);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
|
||||
if (expectedErrorPath) {
|
||||
expect(result.error?.issues[0].path).toEqual(expectedErrorPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
import { z } from 'zod';
|
||||
import { Z } from 'zod-class';
|
||||
|
||||
import { projectRelationSchema } from '../../schemas/project.schema';
|
||||
|
||||
export class AddUsersToProjectDto extends Z.class({
|
||||
relations: z.array(projectRelationSchema).min(1),
|
||||
}) {}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { projectRoleSchema } from '@n8n/permissions';
|
||||
import { Z } from 'zod-class';
|
||||
|
||||
export class ChangeUserRoleInProject extends Z.class({
|
||||
role: projectRoleSchema.exclude(['project:personalOwner']),
|
||||
}) {}
|
||||
@@ -13,7 +13,7 @@ export const projectIconSchema = z.object({
|
||||
export type ProjectIcon = z.infer<typeof projectIconSchema>;
|
||||
|
||||
export const projectRelationSchema = z.object({
|
||||
userId: z.string(),
|
||||
role: projectRoleSchema,
|
||||
userId: z.string().min(1),
|
||||
role: projectRoleSchema.exclude(['project:personalOwner']),
|
||||
});
|
||||
export type ProjectRelation = z.infer<typeof projectRelationSchema>;
|
||||
|
||||
Reference in New Issue
Block a user