diff --git a/packages/backend/bin/database/utils.js b/packages/backend/bin/database/utils.js index 0a3ae129..5b1ca166 100644 --- a/packages/backend/bin/database/utils.js +++ b/packages/backend/bin/database/utils.js @@ -10,7 +10,7 @@ import process from 'process'; async function fetchAdminRole() { const role = await Role.query() .where({ - key: 'admin', + name: 'Admin', }) .limit(1) .first(); diff --git a/packages/backend/src/controllers/api/v1/admin/apps/create-auth-client.ee.test.js b/packages/backend/src/controllers/api/v1/admin/apps/create-auth-client.ee.test.js index 18728650..57afc704 100644 --- a/packages/backend/src/controllers/api/v1/admin/apps/create-auth-client.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/apps/create-auth-client.ee.test.js @@ -15,7 +15,7 @@ describe('POST /api/v1/admin/apps/:appKey/auth-clients', () => { beforeEach(async () => { vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); - adminRole = await createRole({ key: 'admin' }); + adminRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: adminRole.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/admin/apps/create-config.ee.test.js b/packages/backend/src/controllers/api/v1/admin/apps/create-config.ee.test.js index 6499e71b..ad9b7600 100644 --- a/packages/backend/src/controllers/api/v1/admin/apps/create-config.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/apps/create-config.ee.test.js @@ -15,7 +15,7 @@ describe('POST /api/v1/admin/apps/:appKey/config', () => { beforeEach(async () => { vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); - adminRole = await createRole({ key: 'admin' }); + adminRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: adminRole.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/admin/apps/get-auth-client.ee.test.js b/packages/backend/src/controllers/api/v1/admin/apps/get-auth-client.ee.test.js index 581c49e7..a3b1c6c4 100644 --- a/packages/backend/src/controllers/api/v1/admin/apps/get-auth-client.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/apps/get-auth-client.ee.test.js @@ -15,7 +15,7 @@ describe('GET /api/v1/admin/apps/:appKey/auth-clients/:appAuthClientId', () => { beforeEach(async () => { vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); - adminRole = await createRole({ key: 'admin' }); + adminRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: adminRole.id }); currentAppAuthClient = await createAppAuthClient({ diff --git a/packages/backend/src/controllers/api/v1/admin/apps/get-auth-clients.ee.test.js b/packages/backend/src/controllers/api/v1/admin/apps/get-auth-clients.ee.test.js index 0dfd472c..58c25bff 100644 --- a/packages/backend/src/controllers/api/v1/admin/apps/get-auth-clients.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/apps/get-auth-clients.ee.test.js @@ -14,7 +14,7 @@ describe('GET /api/v1/admin/apps/:appKey/auth-clients', () => { beforeEach(async () => { vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); - adminRole = await createRole({ key: 'admin' }); + adminRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: adminRole.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/admin/apps/update-auth-client.ee.test.js b/packages/backend/src/controllers/api/v1/admin/apps/update-auth-client.ee.test.js index 2284bde0..f1a7bccd 100644 --- a/packages/backend/src/controllers/api/v1/admin/apps/update-auth-client.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/apps/update-auth-client.ee.test.js @@ -17,7 +17,7 @@ describe('PATCH /api/v1/admin/apps/:appKey/auth-clients', () => { beforeEach(async () => { vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); - adminRole = await createRole({ key: 'admin' }); + adminRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: adminRole.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/admin/apps/update-config.ee.test.js b/packages/backend/src/controllers/api/v1/admin/apps/update-config.ee.test.js index a6cb0d07..20d7f8ae 100644 --- a/packages/backend/src/controllers/api/v1/admin/apps/update-config.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/apps/update-config.ee.test.js @@ -15,7 +15,7 @@ describe('PATCH /api/v1/admin/apps/:appKey/config', () => { beforeEach(async () => { vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); - adminRole = await createRole({ key: 'admin' }); + adminRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: adminRole.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/admin/config/update.ee.test.js b/packages/backend/src/controllers/api/v1/admin/config/update.ee.test.js index de9a6bb9..465978de 100644 --- a/packages/backend/src/controllers/api/v1/admin/config/update.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/config/update.ee.test.js @@ -14,7 +14,7 @@ describe('PATCH /api/v1/admin/config', () => { beforeEach(async () => { vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); - adminRole = await createRole({ key: 'admin' }); + adminRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: adminRole.id }); token = await createAuthTokenByUserId(currentUser.id); 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 index bbeba16b..cac504fc 100644 --- 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 @@ -11,7 +11,7 @@ describe('GET /api/v1/admin/permissions/catalog', () => { let role, currentUser, token; beforeEach(async () => { - role = await createRole({ key: 'admin' }); + role = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: role.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/admin/roles/create-role.ee.js b/packages/backend/src/controllers/api/v1/admin/roles/create-role.ee.js new file mode 100644 index 00000000..124de644 --- /dev/null +++ b/packages/backend/src/controllers/api/v1/admin/roles/create-role.ee.js @@ -0,0 +1,22 @@ +import { renderObject } from '../../../../../helpers/renderer.js'; +import Role from '../../../../../models/role.js'; + +export default async (request, response) => { + const roleData = roleParams(request); + + const roleWithPermissions = await Role.query().insertGraphAndFetch(roleData, { + relate: ['permissions'], + }); + + renderObject(response, roleWithPermissions, { status: 201 }); +}; + +const roleParams = (request) => { + const { name, description, permissions } = request.body; + + return { + name, + description, + permissions, + }; +}; diff --git a/packages/backend/src/controllers/api/v1/admin/roles/create-role.ee.test.js b/packages/backend/src/controllers/api/v1/admin/roles/create-role.ee.test.js new file mode 100644 index 00000000..6dcd5bcf --- /dev/null +++ b/packages/backend/src/controllers/api/v1/admin/roles/create-role.ee.test.js @@ -0,0 +1,109 @@ +import { vi, describe, it, expect, beforeEach } from 'vitest'; +import request from 'supertest'; +import app from '../../../../../app.js'; +import Role from '../../../../../models/role.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 createRoleMock from '../../../../../../test/mocks/rest/api/v1/admin/roles/create-role.ee.js'; +import * as license from '../../../../../helpers/license.ee.js'; + +describe('POST /api/v1/admin/roles', () => { + let role, currentUser, token; + + beforeEach(async () => { + vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); + + role = await createRole({ name: 'Admin' }); + currentUser = await createUser({ roleId: role.id }); + + token = await createAuthTokenByUserId(currentUser.id); + }); + + it('should return the created role along with permissions', async () => { + const roleData = { + name: 'Viewer', + description: '', + permissions: [ + { + action: 'read', + subject: 'Flow', + conditions: ['isCreator'], + }, + ], + }; + + const response = await request(app) + .post('/api/v1/admin/roles') + .set('Authorization', token) + .send(roleData) + .expect(201); + + const createdRole = await Role.query() + .withGraphFetched({ permissions: true }) + .findOne({ name: 'Viewer' }) + .throwIfNotFound(); + + const expectedPayload = await createRoleMock( + { + ...createdRole, + ...roleData, + isAdmin: createdRole.isAdmin, + }, + [ + { + ...createdRole.permissions[0], + ...roleData.permissions[0], + }, + ] + ); + + expect(response.body).toEqual(expectedPayload); + }); + + it('should return unprocessable entity response for invalid role data', async () => { + const roleData = { + description: '', + permissions: [], + }; + + const response = await request(app) + .post('/api/v1/admin/roles') + .set('Authorization', token) + .send(roleData) + .expect(422); + + expect(response.body).toStrictEqual({ + errors: { + name: ["must have required property 'name'"], + }, + meta: { + type: 'ModelValidation', + }, + }); + }); + + it('should return unprocessable entity response for duplicate role', async () => { + await createRole({ name: 'Viewer' }); + + const roleData = { + name: 'Viewer', + permissions: [], + }; + + const response = await request(app) + .post('/api/v1/admin/roles') + .set('Authorization', token) + .send(roleData) + .expect(422); + + expect(response.body).toStrictEqual({ + errors: { + name: ["'name' must be unique."], + }, + meta: { + type: 'UniqueViolationError', + }, + }); + }); +}); 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 index 020539f4..15c688f7 100644 --- 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 @@ -13,7 +13,7 @@ describe('GET /api/v1/admin/roles/:roleId', () => { let role, currentUser, token, permissionOne, permissionTwo; beforeEach(async () => { - role = await createRole({ key: 'admin' }); + role = await createRole({ name: 'Admin' }); permissionOne = await createPermission({ roleId: role.id }); permissionTwo = await createPermission({ roleId: role.id }); currentUser = await createUser({ roleId: role.id }); diff --git a/packages/backend/src/controllers/api/v1/admin/roles/get-roles.ee.test.js b/packages/backend/src/controllers/api/v1/admin/roles/get-roles.ee.test.js index 6f22f50b..e07a22ea 100644 --- a/packages/backend/src/controllers/api/v1/admin/roles/get-roles.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/roles/get-roles.ee.test.js @@ -11,8 +11,8 @@ describe('GET /api/v1/admin/roles', () => { let roleOne, roleTwo, currentUser, token; beforeEach(async () => { - roleOne = await createRole({ key: 'admin' }); - roleTwo = await createRole({ key: 'user' }); + roleOne = await createRole({ name: 'Admin' }); + roleTwo = await createRole({ name: 'User' }); currentUser = await createUser({ roleId: roleOne.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/create-saml-auth-provider.ee.test.js b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/create-saml-auth-provider.ee.test.js index 517b59d6..eff660d9 100644 --- a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/create-saml-auth-provider.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/create-saml-auth-provider.ee.test.js @@ -13,7 +13,7 @@ describe('POST /api/v1/admin/saml-auth-provider', () => { beforeEach(async () => { vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); - role = await createRole({ key: 'admin' }); + role = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: role.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-role-mappings.ee.test.js b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-role-mappings.ee.test.js index 42f37a3a..e9c54912 100644 --- a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-role-mappings.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-role-mappings.ee.test.js @@ -13,7 +13,7 @@ describe('GET /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mapping let roleMappingOne, roleMappingTwo, samlAuthProvider, currentUser, token; beforeEach(async () => { - const role = await createRole({ key: 'admin' }); + const role = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: role.id }); samlAuthProvider = await createSamlAuthProvider(); diff --git a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-saml-auth-provider.ee.test.js b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-saml-auth-provider.ee.test.js index adddb53d..de12c036 100644 --- a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-saml-auth-provider.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-saml-auth-provider.ee.test.js @@ -13,7 +13,7 @@ describe('GET /api/v1/admin/saml-auth-provider/:samlAuthProviderId', () => { let samlAuthProvider, currentUser, token; beforeEach(async () => { - const role = await createRole({ key: 'admin' }); + const role = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: role.id }); samlAuthProvider = await createSamlAuthProvider(); diff --git a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-saml-auth-providers.ee.test.js b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-saml-auth-providers.ee.test.js index ca2106e2..0818f4da 100644 --- a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-saml-auth-providers.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-saml-auth-providers.ee.test.js @@ -12,7 +12,7 @@ describe('GET /api/v1/admin/saml-auth-providers', () => { let samlAuthProviderOne, samlAuthProviderTwo, currentUser, token; beforeEach(async () => { - const role = await createRole({ key: 'admin' }); + const role = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: role.id }); samlAuthProviderOne = await createSamlAuthProvider(); diff --git a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-saml-auth-provider.ee.test.js b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-saml-auth-provider.ee.test.js index f8c858f1..d3fa960f 100644 --- a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-saml-auth-provider.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-saml-auth-provider.ee.test.js @@ -15,7 +15,7 @@ describe('PATCH /api/v1/admin/saml-auth-provider/:samlAuthProviderId', () => { beforeEach(async () => { vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); - role = await createRole({ key: 'admin' }); + role = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: role.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/admin/users/delete-user.test.js b/packages/backend/src/controllers/api/v1/admin/users/delete-user.test.js index 8e19a4c3..9ef9f058 100644 --- a/packages/backend/src/controllers/api/v1/admin/users/delete-user.test.js +++ b/packages/backend/src/controllers/api/v1/admin/users/delete-user.test.js @@ -10,7 +10,7 @@ describe('DELETE /api/v1/admin/users/:userId', () => { let currentUser, currentUserRole, anotherUser, token; beforeEach(async () => { - currentUserRole = await createRole({ key: 'admin' }); + currentUserRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: currentUserRole.id }); anotherUser = await createUser(); diff --git a/packages/backend/src/controllers/api/v1/admin/users/get-user.ee.test.js b/packages/backend/src/controllers/api/v1/admin/users/get-user.ee.test.js index 93ee5053..2ed6e9e8 100644 --- a/packages/backend/src/controllers/api/v1/admin/users/get-user.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/users/get-user.ee.test.js @@ -12,7 +12,7 @@ describe('GET /api/v1/admin/users/:userId', () => { let currentUser, currentUserRole, anotherUser, anotherUserRole, token; beforeEach(async () => { - currentUserRole = await createRole({ key: 'admin' }); + currentUserRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: currentUserRole.id }); anotherUser = await createUser(); diff --git a/packages/backend/src/controllers/api/v1/admin/users/get-users.ee.test.js b/packages/backend/src/controllers/api/v1/admin/users/get-users.ee.test.js index a7528de0..f3f0bcb7 100644 --- a/packages/backend/src/controllers/api/v1/admin/users/get-users.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/users/get-users.ee.test.js @@ -10,7 +10,7 @@ describe('GET /api/v1/admin/users', () => { let currentUser, currentUserRole, anotherUser, anotherUserRole, token; beforeEach(async () => { - currentUserRole = await createRole({ key: 'admin' }); + currentUserRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: currentUserRole.id, @@ -18,7 +18,6 @@ describe('GET /api/v1/admin/users', () => { }); anotherUserRole = await createRole({ - key: 'anotherUser', name: 'Another user role', }); diff --git a/packages/backend/src/controllers/api/v1/admin/users/update-user.ee.test.js b/packages/backend/src/controllers/api/v1/admin/users/update-user.ee.test.js index 67fd8ecc..a5f4d26b 100644 --- a/packages/backend/src/controllers/api/v1/admin/users/update-user.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/users/update-user.ee.test.js @@ -11,7 +11,7 @@ describe('PATCH /api/v1/admin/users/:userId', () => { let currentUser, adminRole, token; beforeEach(async () => { - adminRole = await createRole({ key: 'admin' }); + adminRole = await createRole({ name: 'Admin' }); currentUser = await createUser({ roleId: adminRole.id }); token = await createAuthTokenByUserId(currentUser.id); diff --git a/packages/backend/src/controllers/api/v1/installation/users/create-user.test.js b/packages/backend/src/controllers/api/v1/installation/users/create-user.test.js index a157dbb3..9c4d9bc6 100644 --- a/packages/backend/src/controllers/api/v1/installation/users/create-user.test.js +++ b/packages/backend/src/controllers/api/v1/installation/users/create-user.test.js @@ -13,8 +13,7 @@ describe('POST /api/v1/installation/users', () => { beforeEach(async () => { adminRole = await createRole({ name: 'Admin', - key: 'admin', - }) + }); }); describe('for incomplete installations', () => { @@ -26,7 +25,7 @@ describe('POST /api/v1/installation/users', () => { .send({ email: 'user@automatisch.io', password: 'password', - fullName: 'Initial admin' + fullName: 'Initial admin', }) .expect(204); @@ -48,7 +47,7 @@ describe('POST /api/v1/installation/users', () => { .send({ email: 'user@automatisch.io', password: 'password', - fullName: 'Initial admin' + fullName: 'Initial admin', }) .expect(403); @@ -71,7 +70,7 @@ describe('POST /api/v1/installation/users', () => { .send({ email: 'user@automatisch.io', password: 'password', - fullName: 'Initial admin' + fullName: 'Initial admin', }) .expect(403); @@ -80,5 +79,5 @@ describe('POST /api/v1/installation/users', () => { expect(user).toBeUndefined(); expect(await Config.isInstallationCompleted()).toBe(true); }); - }) + }); }); diff --git a/packages/backend/src/db/migrations/20240903110620_make_role_name_unique.js b/packages/backend/src/db/migrations/20240903110620_make_role_name_unique.js new file mode 100644 index 00000000..bdccb76a --- /dev/null +++ b/packages/backend/src/db/migrations/20240903110620_make_role_name_unique.js @@ -0,0 +1,11 @@ +export async function up(knex) { + return await knex.schema.alterTable('roles', (table) => { + table.unique('name'); + }); +} + +export async function down(knex) { + return await knex.schema.alterTable('roles', function (table) { + table.dropUnique('name'); + }); +} diff --git a/packages/backend/src/db/migrations/20240904091615_remove_key_column_in_roles.js b/packages/backend/src/db/migrations/20240904091615_remove_key_column_in_roles.js new file mode 100644 index 00000000..d8c87132 --- /dev/null +++ b/packages/backend/src/db/migrations/20240904091615_remove_key_column_in_roles.js @@ -0,0 +1,19 @@ +export async function up(knex) { + return await knex.schema.alterTable('roles', (table) => { + table.dropColumn('key'); + }); +} + +export async function down(knex) { + await knex.schema.alterTable('roles', (table) => { + table.string('key'); + }); + + await knex('roles').update({ + key: knex.raw('LOWER(??)', ['name']), + }); + + return await knex.schema.alterTable('roles', (table) => { + table.string('key').notNullable().alter(); + }); +} diff --git a/packages/backend/src/graphql/mutation-resolvers.js b/packages/backend/src/graphql/mutation-resolvers.js index ada214e7..509fb5ad 100644 --- a/packages/backend/src/graphql/mutation-resolvers.js +++ b/packages/backend/src/graphql/mutation-resolvers.js @@ -1,5 +1,4 @@ import createConnection from './mutations/create-connection.js'; -import createRole from './mutations/create-role.ee.js'; import createStep from './mutations/create-step.js'; import createUser from './mutations/create-user.ee.js'; import deleteFlow from './mutations/delete-flow.js'; @@ -26,7 +25,6 @@ import deleteCurrentUser from './mutations/delete-current-user.ee.js'; const mutationResolvers = { createConnection, createFlow, - createRole, createStep, createUser, deleteCurrentUser, diff --git a/packages/backend/src/graphql/mutations/create-role.ee.js b/packages/backend/src/graphql/mutations/create-role.ee.js deleted file mode 100644 index 7e85207b..00000000 --- a/packages/backend/src/graphql/mutations/create-role.ee.js +++ /dev/null @@ -1,29 +0,0 @@ -import kebabCase from 'lodash/kebabCase.js'; -import Role from '../../models/role.js'; - -const createRole = async (_parent, params, context) => { - context.currentUser.can('create', 'Role'); - - const { name, description, permissions } = params.input; - const key = kebabCase(name); - - const existingRole = await Role.query().findOne({ key }); - - if (existingRole) { - throw new Error('Role already exists!'); - } - - return await Role.query() - .insertGraph( - { - key, - name, - description, - permissions, - }, - { relate: ['permissions'] } - ) - .returning('*'); -}; - -export default createRole; diff --git a/packages/backend/src/graphql/mutations/create-user.ee.js b/packages/backend/src/graphql/mutations/create-user.ee.js index 13fd2f17..2c3fd3fc 100644 --- a/packages/backend/src/graphql/mutations/create-user.ee.js +++ b/packages/backend/src/graphql/mutations/create-user.ee.js @@ -32,7 +32,7 @@ const createUser = async (_parent, params, context) => { userPayload.roleId = params.input.role.id; } catch { // void - const role = await Role.query().findOne({ key: 'admin' }); + const role = await Role.findAdmin(); userPayload.roleId = role.id; } diff --git a/packages/backend/src/graphql/mutations/register-user.ee.js b/packages/backend/src/graphql/mutations/register-user.ee.js index e734e763..c084af9b 100644 --- a/packages/backend/src/graphql/mutations/register-user.ee.js +++ b/packages/backend/src/graphql/mutations/register-user.ee.js @@ -15,7 +15,7 @@ const registerUser = async (_parent, params) => { throw new Error('User already exists!'); } - const role = await Role.query().findOne({ key: 'user' }); + const role = await Role.query().findOne({ name: 'User' }); const user = await User.query().insert({ fullName, diff --git a/packages/backend/src/graphql/schema.graphql b/packages/backend/src/graphql/schema.graphql index 0765d3ce..2735654e 100644 --- a/packages/backend/src/graphql/schema.graphql +++ b/packages/backend/src/graphql/schema.graphql @@ -4,7 +4,6 @@ type Query { type Mutation { createConnection(input: CreateConnectionInput): Connection createFlow(input: CreateFlowInput): Flow - createRole(input: CreateRoleInput): Role createStep(input: CreateStepInput): Step createUser(input: CreateUserInput): UserWithAcceptInvitationUrl deleteCurrentUser: Boolean @@ -342,12 +341,6 @@ input PermissionInput { conditions: [String] } -input CreateRoleInput { - name: String! - description: String - permissions: [PermissionInput] -} - input UpdateRoleInput { id: String! name: String! diff --git a/packages/backend/src/models/role.js b/packages/backend/src/models/role.js index 08b19673..0b0d7852 100644 --- a/packages/backend/src/models/role.js +++ b/packages/backend/src/models/role.js @@ -7,22 +7,17 @@ class Role extends Base { static jsonSchema = { type: 'object', - required: ['name', 'key'], + required: ['name'], properties: { id: { type: 'string', format: 'uuid' }, name: { type: 'string', minLength: 1 }, - key: { type: 'string', minLength: 1 }, description: { type: ['string', 'null'], maxLength: 255 }, createdAt: { type: 'string' }, updatedAt: { type: 'string' }, }, }; - static get virtualAttributes() { - return ['isAdmin']; - } - static relationMappings = () => ({ users: { relation: Base.HasManyRelation, @@ -42,12 +37,16 @@ class Role extends Base { }, }); + static get virtualAttributes() { + return ['isAdmin']; + } + get isAdmin() { - return this.key === 'admin'; + return this.name === 'Admin'; } static async findAdmin() { - return await this.query().findOne({ key: 'admin' }); + return await this.query().findOne({ name: 'Admin' }); } } 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 238856e8..6ec88967 100644 --- a/packages/backend/src/routes/api/v1/admin/roles.ee.js +++ b/packages/backend/src/routes/api/v1/admin/roles.ee.js @@ -2,11 +2,20 @@ 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 createRoleAction from '../../../../controllers/api/v1/admin/roles/create-role.ee.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(); +router.post( + '/', + authenticateUser, + authorizeAdmin, + checkIsEnterprise, + createRoleAction +); + router.get( '/', authenticateUser, diff --git a/packages/backend/test/factories/role.js b/packages/backend/test/factories/role.js index b06d93db..7db3eff2 100644 --- a/packages/backend/test/factories/role.js +++ b/packages/backend/test/factories/role.js @@ -1,8 +1,10 @@ +import { faker } from '@faker-js/faker'; import Role from '../../src/models/role'; export const createRole = async (params = {}) => { - params.name = params?.name || 'Viewer'; - params.key = params?.key || 'viewer'; + const name = faker.lorem.word(); + + params.name = params?.name || name; const role = await Role.query().insertAndFetch(params); diff --git a/packages/backend/test/mocks/rest/api/v1/admin/roles/create-role.ee.js b/packages/backend/test/mocks/rest/api/v1/admin/roles/create-role.ee.js new file mode 100644 index 00000000..caa5b4c8 --- /dev/null +++ b/packages/backend/test/mocks/rest/api/v1/admin/roles/create-role.ee.js @@ -0,0 +1,32 @@ +const createRoleMock = async (role, permissions = []) => { + const data = { + id: role.id, + name: role.name, + isAdmin: role.isAdmin, + description: role.description, + createdAt: role.createdAt.getTime(), + updatedAt: role.updatedAt.getTime(), + permissions: permissions.map((permission) => ({ + id: permission.id, + action: permission.action, + conditions: permission.conditions, + roleId: permission.roleId, + subject: permission.subject, + createdAt: permission.createdAt.getTime(), + updatedAt: permission.updatedAt.getTime(), + })), + }; + + return { + data: data, + meta: { + count: 1, + currentPage: null, + isArray: false, + totalPages: null, + type: 'Role', + }, + }; +}; + +export default createRoleMock; 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 index 05942763..df46557e 100644 --- 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 @@ -1,7 +1,6 @@ const getRoleMock = async (role, permissions) => { const data = { id: role.id, - key: role.key, name: role.name, isAdmin: role.isAdmin, description: role.description, diff --git a/packages/backend/test/mocks/rest/api/v1/admin/roles/get-roles.ee.js b/packages/backend/test/mocks/rest/api/v1/admin/roles/get-roles.ee.js index fdb7a05b..b37e2f7e 100644 --- a/packages/backend/test/mocks/rest/api/v1/admin/roles/get-roles.ee.js +++ b/packages/backend/test/mocks/rest/api/v1/admin/roles/get-roles.ee.js @@ -2,7 +2,6 @@ const getRolesMock = async (roles) => { const data = roles.map((role) => { return { id: role.id, - key: role.key, name: role.name, isAdmin: role.isAdmin, description: role.description, diff --git a/packages/backend/test/mocks/rest/api/v1/admin/users/update-user.js b/packages/backend/test/mocks/rest/api/v1/admin/users/update-user.js index bc692875..f09b2a15 100644 --- a/packages/backend/test/mocks/rest/api/v1/admin/users/update-user.js +++ b/packages/backend/test/mocks/rest/api/v1/admin/users/update-user.js @@ -9,7 +9,6 @@ const updateUserMock = (user, role) => { updatedAt: user.updatedAt.getTime(), role: { id: role.id, - key: role.key, name: role.name, isAdmin: role.isAdmin, createdAt: role.createdAt.getTime(), diff --git a/packages/web/src/graphql/mutations/create-role.ee.js b/packages/web/src/graphql/mutations/create-role.ee.js deleted file mode 100644 index be519627..00000000 --- a/packages/web/src/graphql/mutations/create-role.ee.js +++ /dev/null @@ -1,11 +0,0 @@ -import { gql } from '@apollo/client'; -export const CREATE_ROLE = gql` - mutation CreateRole($input: CreateRoleInput) { - createRole(input: $input) { - id - key - name - description - } - } -`; diff --git a/packages/web/src/hooks/useAdminCreateRole.js b/packages/web/src/hooks/useAdminCreateRole.js new file mode 100644 index 00000000..756d4cc5 --- /dev/null +++ b/packages/web/src/hooks/useAdminCreateRole.js @@ -0,0 +1,21 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import api from 'helpers/api'; + +export default function useAdminCreateRole() { + const queryClient = useQueryClient(); + + const query = useMutation({ + mutationFn: async (payload) => { + const { data } = await api.post('/v1/admin/roles', payload); + + return data; + }, + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: ['apps', 'roles'], + }); + }, + }); + + return query; +} diff --git a/packages/web/src/pages/CreateRole/index.ee.jsx b/packages/web/src/pages/CreateRole/index.ee.jsx index 62e54aff..a7767e76 100644 --- a/packages/web/src/pages/CreateRole/index.ee.jsx +++ b/packages/web/src/pages/CreateRole/index.ee.jsx @@ -1,4 +1,3 @@ -import { useMutation } from '@apollo/client'; import LoadingButton from '@mui/lab/LoadingButton'; import Grid from '@mui/material/Grid'; import Stack from '@mui/material/Stack'; @@ -6,42 +5,55 @@ import PermissionCatalogField from 'components/PermissionCatalogField/index.ee'; import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar'; import * as React from 'react'; import { useNavigate } from 'react-router-dom'; + import Container from 'components/Container'; import Form from 'components/Form'; import PageTitle from 'components/PageTitle'; import TextField from 'components/TextField'; import * as URLS from 'config/urls'; -import { CREATE_ROLE } from 'graphql/mutations/create-role.ee'; import { getPermissions } from 'helpers/computePermissions.ee'; import useFormatMessage from 'hooks/useFormatMessage'; +import useAdminCreateRole from 'hooks/useAdminCreateRole'; + export default function CreateRole() { const navigate = useNavigate(); const formatMessage = useFormatMessage(); - const [createRole, { loading }] = useMutation(CREATE_ROLE); const enqueueSnackbar = useEnqueueSnackbar(); + const { mutateAsync: createRole, isPending: isCreateRolePending } = + useAdminCreateRole(); + const handleRoleCreation = async (roleData) => { try { const permissions = getPermissions(roleData.computedPermissions); + await createRole({ - variables: { - input: { - name: roleData.name, - description: roleData.description, - permissions, - }, - }, + name: roleData.name, + description: roleData.description, + permissions, }); + enqueueSnackbar(formatMessage('createRole.successfullyCreated'), { variant: 'success', SnackbarProps: { 'data-test': 'snackbar-create-role-success', }, }); + navigate(URLS.ROLES); } catch (error) { - throw new Error('Failed while creating!'); + const errors = Object.values(error.response.data.errors); + + for (const [errorMessage] of errors) { + enqueueSnackbar(errorMessage, { + variant: 'error', + SnackbarProps: { + 'data-test': 'snackbar-error', + }, + }); + } } }; + return ( @@ -79,7 +91,7 @@ export default function CreateRole() { variant="contained" color="primary" sx={{ boxShadow: 2 }} - loading={loading} + loading={isCreateRolePending} data-test="create-button" > {formatMessage('createRole.submit')}