diff --git a/packages/backend/src/controllers/api/v1/admin/permissions/get-permissions-catalog.ee.js b/packages/backend/src/controllers/api/v1/admin/permissions/get-permissions-catalog.ee.js new file mode 100644 index 00000000..232e33ea --- /dev/null +++ b/packages/backend/src/controllers/api/v1/admin/permissions/get-permissions-catalog.ee.js @@ -0,0 +1,6 @@ +import { renderObject } from '../../../../../helpers/renderer.js'; +import permissionCatalog from '../../../../../helpers/permission-catalog.ee.js'; + +export default async (request, response) => { + renderObject(response, permissionCatalog); +}; diff --git a/packages/backend/src/controllers/api/v1/admin/permissions/get-permissions-catalog.ee.test.js b/packages/backend/src/controllers/api/v1/admin/permissions/get-permissions-catalog.ee.test.js new file mode 100644 index 00000000..44a791dc --- /dev/null +++ b/packages/backend/src/controllers/api/v1/admin/permissions/get-permissions-catalog.ee.test.js @@ -0,0 +1,32 @@ +import { vi, describe, it, expect, beforeEach } from 'vitest'; +import request from 'supertest'; +import app from '../../../../../app.js'; +import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; +import { createRole } from '../../../../../../test/factories/role.js'; +import { createUser } from '../../../../../../test/factories/user.js'; +import getPermissionsCatalogMock from '../../../../../../test/mocks/rest/api/v1/admin/permissions/get-permissions-catalog.ee.js'; +import * as license from '../../../../../helpers/license.ee.js'; + +describe('GET /api/v1/admin/permissions/catalog', () => { + let role, currentUser, token; + + beforeEach(async () => { + role = await createRole({ key: 'admin' }); + currentUser = await createUser({ roleId: role.id }); + + token = createAuthTokenByUserId(currentUser.id); + }); + + it('should return roles', async () => { + vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); + + const response = await request(app) + .get('/api/v1/admin/permissions/catalog') + .set('Authorization', token) + .expect(200); + + const expectedPayload = await getPermissionsCatalogMock(); + + expect(response.body).toEqual(expectedPayload); + }); +}); diff --git a/packages/backend/src/controllers/api/v1/admin/roles/get-role.ee.js b/packages/backend/src/controllers/api/v1/admin/roles/get-role.ee.js new file mode 100644 index 00000000..57733ae8 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/admin/roles/get-role.ee.js @@ -0,0 +1,16 @@ +import { renderObject } from '../../../../../helpers/renderer.js'; +import Role from '../../../../../models/role.js'; + +export default async (request, response) => { + const role = await Role.query() + .leftJoinRelated({ + permissions: true, + }) + .withGraphFetched({ + permissions: true, + }) + .findById(request.params.roleId) + .throwIfNotFound(); + + renderObject(response, role); +}; diff --git a/packages/backend/src/controllers/api/v1/admin/roles/get-role.ee.test.js b/packages/backend/src/controllers/api/v1/admin/roles/get-role.ee.test.js new file mode 100644 index 00000000..9a4dca01 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/admin/roles/get-role.ee.test.js @@ -0,0 +1,38 @@ +import { vi, describe, it, expect, beforeEach } from 'vitest'; +import request from 'supertest'; +import app from '../../../../../app.js'; +import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js'; +import { createRole } from '../../../../../../test/factories/role.js'; +import { createUser } from '../../../../../../test/factories/user.js'; +import { createPermission } from '../../../../../../test/factories/permission.js'; +import getRoleMock from '../../../../../../test/mocks/rest/api/v1/admin/roles/get-role.ee.js'; +import * as license from '../../../../../helpers/license.ee.js'; + +describe('GET /api/v1/admin/roles/:roleId', () => { + let role, currentUser, token, permissionOne, permissionTwo; + + beforeEach(async () => { + role = await createRole({ key: 'admin' }); + permissionOne = await createPermission({ roleId: role.id }); + permissionTwo = await createPermission({ roleId: role.id }); + currentUser = await createUser({ roleId: role.id }); + + token = createAuthTokenByUserId(currentUser.id); + }); + + it('should return roles', async () => { + vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); + + const response = await request(app) + .get(`/api/v1/admin/roles/${role.id}`) + .set('Authorization', token) + .expect(200); + + const expectedPayload = await getRoleMock(role, [ + permissionOne, + permissionTwo, + ]); + + expect(response.body).toEqual(expectedPayload); + }); +}); diff --git a/packages/backend/src/routes/api/v1/admin/permissions.ee.js b/packages/backend/src/routes/api/v1/admin/permissions.ee.js new file mode 100644 index 00000000..91a8486c --- /dev/null +++ b/packages/backend/src/routes/api/v1/admin/permissions.ee.js @@ -0,0 +1,17 @@ +import { Router } from 'express'; +import { authenticateUser } from '../../../../helpers/authentication.js'; +import { authorizeAdmin } from '../../../../helpers/authorization.js'; +import { checkIsEnterprise } from '../../../../helpers/check-is-enterprise.js'; +import getPermissionsCatalogAction from '../../../../controllers/api/v1/admin/permissions/get-permissions-catalog.ee.js'; + +const router = Router(); + +router.get( + '/catalog', + authenticateUser, + authorizeAdmin, + checkIsEnterprise, + getPermissionsCatalogAction +); + +export default router; diff --git a/packages/backend/src/routes/api/v1/admin/roles.ee.js b/packages/backend/src/routes/api/v1/admin/roles.ee.js index bdcc7299..238856e8 100644 --- a/packages/backend/src/routes/api/v1/admin/roles.ee.js +++ b/packages/backend/src/routes/api/v1/admin/roles.ee.js @@ -3,6 +3,7 @@ import { authenticateUser } from '../../../../helpers/authentication.js'; import { authorizeAdmin } from '../../../../helpers/authorization.js'; import { checkIsEnterprise } from '../../../../helpers/check-is-enterprise.js'; import getRolesAction from '../../../../controllers/api/v1/admin/roles/get-roles.ee.js'; +import getRoleAction from '../../../../controllers/api/v1/admin/roles/get-role.ee.js'; const router = Router(); @@ -14,4 +15,12 @@ router.get( getRolesAction ); +router.get( + '/:roleId', + authenticateUser, + authorizeAdmin, + checkIsEnterprise, + getRoleAction +); + export default router; diff --git a/packages/backend/src/routes/index.js b/packages/backend/src/routes/index.js index 6f78042d..1582d935 100644 --- a/packages/backend/src/routes/index.js +++ b/packages/backend/src/routes/index.js @@ -7,6 +7,7 @@ import automatischRouter from './api/v1/automatisch.js'; import usersRouter from './api/v1/users.js'; import samlAuthProvidersRouter from './api/v1/admin/saml-auth-providers.ee.js'; import rolesRouter from './api/v1/admin/roles.ee.js'; +import permissionsRouter from './api/v1/admin/permissions.ee.js'; const router = Router(); @@ -18,5 +19,6 @@ router.use('/api/v1/automatisch', automatischRouter); router.use('/api/v1/users', usersRouter); router.use('/api/v1/admin/saml-auth-providers', samlAuthProvidersRouter); router.use('/api/v1/admin/roles', rolesRouter); +router.use('/api/v1/admin/permissions', permissionsRouter); export default router; diff --git a/packages/backend/src/serializers/role.js b/packages/backend/src/serializers/role.js index 1835a093..cc3be89c 100644 --- a/packages/backend/src/serializers/role.js +++ b/packages/backend/src/serializers/role.js @@ -1,5 +1,7 @@ +import permissionSerializer from './permission.js'; + const roleSerializer = (role) => { - return { + let roleData = { id: role.id, name: role.name, key: role.key, @@ -8,6 +10,14 @@ const roleSerializer = (role) => { updatedAt: role.updatedAt, isAdmin: role.isAdmin, }; + + if (role.permissions) { + roleData.permissions = role.permissions.map((permission) => + permissionSerializer(permission) + ); + } + + return roleData; }; export default roleSerializer; diff --git a/packages/backend/src/serializers/role.test.js b/packages/backend/src/serializers/role.test.js index 92a0d6b5..900ad9c2 100644 --- a/packages/backend/src/serializers/role.test.js +++ b/packages/backend/src/serializers/role.test.js @@ -1,12 +1,25 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { createRole } from '../../test/factories/role'; import roleSerializer from './role'; +import { createPermission } from '../../test/factories/permission'; describe('roleSerializer', () => { - let role; + let role, permissionOne, permissionTwo; beforeEach(async () => { role = await createRole(); + + permissionOne = await createPermission({ + roleId: role.id, + action: 'read', + subject: 'User', + }); + + permissionTwo = await createPermission({ + roleId: role.id, + action: 'read', + subject: 'Role', + }); }); it('should return role data', async () => { @@ -22,4 +35,14 @@ describe('roleSerializer', () => { expect(roleSerializer(role)).toEqual(expectedPayload); }); + + it('should return role data with the permissions', async () => { + role.permissions = [permissionOne, permissionTwo]; + + const expectedPayload = { + permissions: [permissionOne, permissionTwo], + }; + + expect(roleSerializer(role)).toMatchObject(expectedPayload); + }); }); diff --git a/packages/backend/test/mocks/rest/api/v1/admin/permissions/get-permissions-catalog.ee.js b/packages/backend/test/mocks/rest/api/v1/admin/permissions/get-permissions-catalog.ee.js new file mode 100644 index 00000000..627bfa33 --- /dev/null +++ b/packages/backend/test/mocks/rest/api/v1/admin/permissions/get-permissions-catalog.ee.js @@ -0,0 +1,64 @@ +const getPermissionsCatalogMock = async () => { + const data = { + actions: [ + { + key: 'create', + label: 'Create', + subjects: ['Connection', 'Flow'], + }, + { + key: 'read', + label: 'Read', + subjects: ['Connection', 'Execution', 'Flow'], + }, + { + key: 'update', + label: 'Update', + subjects: ['Connection', 'Flow'], + }, + { + key: 'delete', + label: 'Delete', + subjects: ['Connection', 'Flow'], + }, + { + key: 'publish', + label: 'Publish', + subjects: ['Flow'], + }, + ], + conditions: [ + { + key: 'isCreator', + label: 'Is creator', + }, + ], + subjects: [ + { + key: 'Connection', + label: 'Connection', + }, + { + key: 'Flow', + label: 'Flow', + }, + { + key: 'Execution', + label: 'Execution', + }, + ], + }; + + return { + data: data, + meta: { + count: 1, + currentPage: null, + isArray: false, + totalPages: null, + type: 'Object', + }, + }; +}; + +export default getPermissionsCatalogMock; diff --git a/packages/backend/test/mocks/rest/api/v1/admin/roles/get-role.ee.js b/packages/backend/test/mocks/rest/api/v1/admin/roles/get-role.ee.js new file mode 100644 index 00000000..ae1505de --- /dev/null +++ b/packages/backend/test/mocks/rest/api/v1/admin/roles/get-role.ee.js @@ -0,0 +1,33 @@ +const getRoleMock = async (role, permissions) => { + const data = { + id: role.id, + key: role.key, + name: role.name, + isAdmin: role.isAdmin, + description: role.description, + createdAt: role.createdAt.toISOString(), + updatedAt: role.updatedAt.toISOString(), + permissions: permissions.map((permission) => ({ + id: permission.id, + action: permission.action, + conditions: permission.conditions, + roleId: permission.roleId, + subject: permission.subject, + createdAt: permission.createdAt.toISOString(), + updatedAt: permission.updatedAt.toISOString(), + })), + }; + + return { + data: data, + meta: { + count: 1, + currentPage: null, + isArray: false, + totalPages: null, + type: 'Role', + }, + }; +}; + +export default getRoleMock;