diff --git a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-role-mappings.ee.js b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-role-mappings.ee.js index 2f7a377f..9c7f5ed7 100644 --- a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-role-mappings.ee.js +++ b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/get-role-mappings.ee.js @@ -7,7 +7,7 @@ export default async (request, response) => { .throwIfNotFound(); const roleMappings = await samlAuthProvider - .$relatedQuery('samlAuthProvidersRoleMappings') + .$relatedQuery('roleMappings') .orderBy('remote_role_name', 'asc'); renderObject(response, roleMappings); diff --git a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js index c0e6afb8..9ca388c1 100644 --- a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js +++ b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js @@ -8,15 +8,14 @@ export default async (request, response) => { .findById(samlAuthProviderId) .throwIfNotFound(); - const samlAuthProvidersRoleMappings = - await samlAuthProvider.updateRoleMappings( - samlAuthProvidersRoleMappingsParams(request) - ); + const roleMappings = await samlAuthProvider.updateRoleMappings( + roleMappingsParams(request) + ); - renderObject(response, samlAuthProvidersRoleMappings); + renderObject(response, roleMappings); }; -const samlAuthProvidersRoleMappingsParams = (request) => { +const roleMappingsParams = (request) => { const roleMappings = request.body; return roleMappings.map(({ roleId, remoteRoleName }) => ({ diff --git a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-role-mappings.ee.test.js b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-role-mappings.ee.test.js index f06a3ba6..523b17ac 100644 --- a/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-role-mappings.ee.test.js +++ b/packages/backend/src/controllers/api/v1/admin/saml-auth-providers/update-role-mappings.ee.test.js @@ -6,7 +6,7 @@ import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by import { createRole } from '../../../../../../test/factories/role.js'; import { createUser } from '../../../../../../test/factories/user.js'; import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js'; -import { createSamlAuthProvidersRoleMapping } from '../../../../../../test/factories/saml-auth-providers-role-mapping.js'; +import { createRoleMapping } from '../../../../../../test/factories/role-mapping.js'; import createRoleMappingsMock from '../../../../../../test/mocks/rest/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js'; import * as license from '../../../../../helpers/license.ee.js'; @@ -21,12 +21,12 @@ describe('PATCH /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mappi samlAuthProvider = await createSamlAuthProvider(); - await createSamlAuthProvidersRoleMapping({ + await createRoleMapping({ samlAuthProviderId: samlAuthProvider.id, remoteRoleName: 'Viewer', }); - await createSamlAuthProvidersRoleMapping({ + await createRoleMapping({ samlAuthProviderId: samlAuthProvider.id, remoteRoleName: 'Editor', }); @@ -64,7 +64,7 @@ describe('PATCH /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mappi it('should delete role mappings when given empty role mappings', async () => { const existingRoleMappings = await samlAuthProvider.$relatedQuery( - 'samlAuthProvidersRoleMappings' + 'roleMappings' ); expect(existingRoleMappings.length).toBe(2); @@ -161,7 +161,7 @@ describe('PATCH /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mappi ]; const roleMappingsBeforeRequest = await samlAuthProvider.$relatedQuery( - 'samlAuthProvidersRoleMappings' + 'roleMappings' ); await request(app) @@ -173,7 +173,7 @@ describe('PATCH /api/v1/admin/saml-auth-providers/:samlAuthProviderId/role-mappi .expect(422); const roleMappingsAfterRequest = await samlAuthProvider.$relatedQuery( - 'samlAuthProvidersRoleMappings' + 'roleMappings' ); expect(roleMappingsBeforeRequest).toStrictEqual(roleMappingsAfterRequest); diff --git a/packages/backend/src/db/migrations/20241125141647_rename_saml_auth_providers_role_mappings_as_role_mappings.js b/packages/backend/src/db/migrations/20241125141647_rename_saml_auth_providers_role_mappings_as_role_mappings.js new file mode 100644 index 00000000..b8abfe85 --- /dev/null +++ b/packages/backend/src/db/migrations/20241125141647_rename_saml_auth_providers_role_mappings_as_role_mappings.js @@ -0,0 +1,52 @@ +export async function up(knex) { + await knex.schema.createTable('role_mappings', (table) => { + table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()')); + table + .uuid('saml_auth_provider_id') + .references('id') + .inTable('saml_auth_providers'); + table.uuid('role_id').references('id').inTable('roles'); + table.string('remote_role_name').notNullable(); + + table.unique(['saml_auth_provider_id', 'remote_role_name']); + + table.timestamps(true, true); + }); + + const existingRoleMappings = await knex('saml_auth_providers_role_mappings'); + + if (existingRoleMappings.length) { + await knex('role_mappings').insert(existingRoleMappings); + } + + return await knex.schema.dropTable('saml_auth_providers_role_mappings'); +} + +export async function down(knex) { + await knex.schema.createTable( + 'saml_auth_providers_role_mappings', + (table) => { + table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()')); + table + .uuid('saml_auth_provider_id') + .references('id') + .inTable('saml_auth_providers'); + table.uuid('role_id').references('id').inTable('roles'); + table.string('remote_role_name').notNullable(); + + table.unique(['saml_auth_provider_id', 'remote_role_name']); + + table.timestamps(true, true); + } + ); + + const existingRoleMappings = await knex('role_mappings'); + + if (existingRoleMappings.length) { + await knex('saml_auth_providers_role_mappings').insert( + existingRoleMappings + ); + } + + return await knex.schema.dropTable('role_mappings'); +} diff --git a/packages/backend/src/helpers/find-or-create-user-by-saml-identity.ee.js b/packages/backend/src/helpers/find-or-create-user-by-saml-identity.ee.js index daeb1206..2349b60d 100644 --- a/packages/backend/src/helpers/find-or-create-user-by-saml-identity.ee.js +++ b/packages/backend/src/helpers/find-or-create-user-by-saml-identity.ee.js @@ -30,7 +30,7 @@ const findOrCreateUserBySamlIdentity = async ( : [mappedUser.role]; const samlAuthProviderRoleMapping = await samlAuthProvider - .$relatedQuery('samlAuthProvidersRoleMappings') + .$relatedQuery('roleMappings') .whereIn('remote_role_name', mappedRoles) .limit(1) .first(); diff --git a/packages/backend/src/models/__snapshots__/role-mapping.ee.test.js.snap b/packages/backend/src/models/__snapshots__/role-mapping.ee.test.js.snap new file mode 100644 index 00000000..fceaa4b0 --- /dev/null +++ b/packages/backend/src/models/__snapshots__/role-mapping.ee.test.js.snap @@ -0,0 +1,30 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`RoleMapping model > jsonSchema should have the correct schema 1`] = ` +{ + "properties": { + "id": { + "format": "uuid", + "type": "string", + }, + "remoteRoleName": { + "minLength": 1, + "type": "string", + }, + "roleId": { + "format": "uuid", + "type": "string", + }, + "samlAuthProviderId": { + "format": "uuid", + "type": "string", + }, + }, + "required": [ + "samlAuthProviderId", + "roleId", + "remoteRoleName", + ], + "type": "object", +} +`; diff --git a/packages/backend/src/models/__snapshots__/saml-auth-providers-role-mapping.ee.test.js.snap b/packages/backend/src/models/__snapshots__/saml-auth-providers-role-mapping.ee.test.js.snap index fca95d14..fceaa4b0 100644 --- a/packages/backend/src/models/__snapshots__/saml-auth-providers-role-mapping.ee.test.js.snap +++ b/packages/backend/src/models/__snapshots__/saml-auth-providers-role-mapping.ee.test.js.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`SamlAuthProvidersRoleMapping model > jsonSchema should have the correct schema 1`] = ` +exports[`RoleMapping model > jsonSchema should have the correct schema 1`] = ` { "properties": { "id": { diff --git a/packages/backend/src/models/saml-auth-providers-role-mapping.ee.js b/packages/backend/src/models/role-mapping.ee.js similarity index 74% rename from packages/backend/src/models/saml-auth-providers-role-mapping.ee.js rename to packages/backend/src/models/role-mapping.ee.js index 00e11bb0..0a5866e9 100644 --- a/packages/backend/src/models/saml-auth-providers-role-mapping.ee.js +++ b/packages/backend/src/models/role-mapping.ee.js @@ -1,8 +1,8 @@ import Base from './base.js'; import SamlAuthProvider from './saml-auth-provider.ee.js'; -class SamlAuthProvidersRoleMapping extends Base { - static tableName = 'saml_auth_providers_role_mappings'; +class RoleMapping extends Base { + static tableName = 'role_mappings'; static jsonSchema = { type: 'object', @@ -21,11 +21,11 @@ class SamlAuthProvidersRoleMapping extends Base { relation: Base.BelongsToOneRelation, modelClass: SamlAuthProvider, join: { - from: 'saml_auth_providers_role_mappings.saml_auth_provider_id', + from: 'role_mappings.saml_auth_provider_id', to: 'saml_auth_providers.id', }, }, }); } -export default SamlAuthProvidersRoleMapping; +export default RoleMapping; diff --git a/packages/backend/src/models/saml-auth-providers-role-mapping.ee.test.js b/packages/backend/src/models/role-mapping.ee.test.js similarity index 56% rename from packages/backend/src/models/saml-auth-providers-role-mapping.ee.test.js rename to packages/backend/src/models/role-mapping.ee.test.js index 2fe26e90..25400112 100644 --- a/packages/backend/src/models/saml-auth-providers-role-mapping.ee.test.js +++ b/packages/backend/src/models/role-mapping.ee.test.js @@ -1,28 +1,26 @@ import { describe, it, expect } from 'vitest'; -import SamlAuthProvidersRoleMapping from '../models/saml-auth-providers-role-mapping.ee'; +import RoleMapping from './role-mapping.ee'; import SamlAuthProvider from './saml-auth-provider.ee'; import Base from './base'; -describe('SamlAuthProvidersRoleMapping model', () => { +describe('RoleMapping model', () => { it('tableName should return correct name', () => { - expect(SamlAuthProvidersRoleMapping.tableName).toBe( - 'saml_auth_providers_role_mappings' - ); + expect(RoleMapping.tableName).toBe('role_mappings'); }); it('jsonSchema should have the correct schema', () => { - expect(SamlAuthProvidersRoleMapping.jsonSchema).toMatchSnapshot(); + expect(RoleMapping.jsonSchema).toMatchSnapshot(); }); it('relationMappings should return correct associations', () => { - const relationMappings = SamlAuthProvidersRoleMapping.relationMappings(); + const relationMappings = RoleMapping.relationMappings(); const expectedRelations = { samlAuthProvider: { relation: Base.BelongsToOneRelation, modelClass: SamlAuthProvider, join: { - from: 'saml_auth_providers_role_mappings.saml_auth_provider_id', + from: 'role_mappings.saml_auth_provider_id', to: 'saml_auth_providers.id', }, }, diff --git a/packages/backend/src/models/saml-auth-provider.ee.js b/packages/backend/src/models/saml-auth-provider.ee.js index 744da1a8..44f6f15e 100644 --- a/packages/backend/src/models/saml-auth-provider.ee.js +++ b/packages/backend/src/models/saml-auth-provider.ee.js @@ -5,7 +5,7 @@ import appConfig from '../config/app.js'; import axios from '../helpers/axios-with-proxy.js'; import Base from './base.js'; import Identity from './identity.ee.js'; -import SamlAuthProvidersRoleMapping from './saml-auth-providers-role-mapping.ee.js'; +import RoleMapping from './role-mapping.ee.js'; class SamlAuthProvider extends Base { static tableName = 'saml_auth_providers'; @@ -53,12 +53,12 @@ class SamlAuthProvider extends Base { to: 'saml_auth_providers.id', }, }, - samlAuthProvidersRoleMappings: { + roleMappings: { relation: Base.HasManyRelation, - modelClass: SamlAuthProvidersRoleMapping, + modelClass: RoleMapping, join: { from: 'saml_auth_providers.id', - to: 'saml_auth_providers_role_mappings.saml_auth_provider_id', + to: 'role_mappings.saml_auth_provider_id', }, }, }); @@ -134,25 +134,22 @@ class SamlAuthProvider extends Base { async updateRoleMappings(roleMappings) { return await SamlAuthProvider.transaction(async (trx) => { - await this.$relatedQuery('samlAuthProvidersRoleMappings', trx).delete(); + await this.$relatedQuery('roleMappings', trx).delete(); if (isEmpty(roleMappings)) { return []; } - const samlAuthProvidersRoleMappingsData = roleMappings.map( - (samlAuthProvidersRoleMapping) => ({ - ...samlAuthProvidersRoleMapping, - samlAuthProviderId: this.id, - }) + const roleMappingsData = roleMappings.map((roleMapping) => ({ + ...roleMapping, + samlAuthProviderId: this.id, + })); + + const newRoleMappings = await RoleMapping.query(trx).insertAndFetch( + roleMappingsData ); - const samlAuthProvidersRoleMappings = - await SamlAuthProvidersRoleMapping.query(trx).insertAndFetch( - samlAuthProvidersRoleMappingsData - ); - - return samlAuthProvidersRoleMappings; + return newRoleMappings; }); } } diff --git a/packages/backend/src/models/saml-auth-provider.ee.test.js b/packages/backend/src/models/saml-auth-provider.ee.test.js index cc20db52..2f94f44e 100644 --- a/packages/backend/src/models/saml-auth-provider.ee.test.js +++ b/packages/backend/src/models/saml-auth-provider.ee.test.js @@ -1,6 +1,6 @@ import { vi, describe, it, expect } from 'vitest'; import SamlAuthProvider from '../models/saml-auth-provider.ee'; -import SamlAuthProvidersRoleMapping from '../models/saml-auth-providers-role-mapping.ee'; +import RoleMapping from '../models/role-mapping.ee'; import Identity from './identity.ee'; import Base from './base'; import appConfig from '../config/app'; @@ -26,12 +26,12 @@ describe('SamlAuthProvider model', () => { to: 'saml_auth_providers.id', }, }, - samlAuthProvidersRoleMappings: { + roleMappings: { relation: Base.HasManyRelation, - modelClass: SamlAuthProvidersRoleMapping, + modelClass: RoleMapping, join: { from: 'saml_auth_providers.id', - to: 'saml_auth_providers_role_mappings.saml_auth_provider_id', + to: 'role_mappings.saml_auth_provider_id', }, }, }; diff --git a/packages/backend/src/serializers/index.js b/packages/backend/src/serializers/index.js index 9a38e5e9..8fdeb888 100644 --- a/packages/backend/src/serializers/index.js +++ b/packages/backend/src/serializers/index.js @@ -26,7 +26,7 @@ const serializers = { Permission: permissionSerializer, AdminSamlAuthProvider: adminSamlAuthProviderSerializer, SamlAuthProvider: samlAuthProviderSerializer, - SamlAuthProvidersRoleMapping: samlAuthProviderRoleMappingSerializer, + RoleMapping: samlAuthProviderRoleMappingSerializer, AppAuthClient: appAuthClientSerializer, AppConfig: appConfigSerializer, Flow: flowSerializer, diff --git a/packages/backend/test/factories/role-mapping.js b/packages/backend/test/factories/role-mapping.js index e9d37fcc..19352dd0 100644 --- a/packages/backend/test/factories/role-mapping.js +++ b/packages/backend/test/factories/role-mapping.js @@ -1,16 +1,15 @@ +import { faker } from '@faker-js/faker'; import { createRole } from './role.js'; +import RoleMapping from '../../src/models/role-mapping.ee.js'; import { createSamlAuthProvider } from './saml-auth-provider.ee.js'; -import SamlAuthProviderRoleMapping from '../../src/models/saml-auth-providers-role-mapping.ee.js'; export const createRoleMapping = async (params = {}) => { - params.roleId = params?.roleId || (await createRole()).id; + params.roleId = params.roleId || (await createRole()).id; params.samlAuthProviderId = - params?.samlAuthProviderId || (await createSamlAuthProvider()).id; + params.samlAuthProviderId || (await createSamlAuthProvider()).id; + params.remoteRoleName = params.remoteRoleName || faker.person.jobType(); - params.remoteRoleName = params?.remoteRoleName || 'User'; + const roleMapping = await RoleMapping.query().insertAndFetch(params); - const samlAuthProviderRoleMapping = - await SamlAuthProviderRoleMapping.query().insertAndFetch(params); - - return samlAuthProviderRoleMapping; + return roleMapping; }; diff --git a/packages/backend/test/factories/saml-auth-providers-role-mapping.js b/packages/backend/test/factories/saml-auth-providers-role-mapping.js deleted file mode 100644 index 72b23c06..00000000 --- a/packages/backend/test/factories/saml-auth-providers-role-mapping.js +++ /dev/null @@ -1,16 +0,0 @@ -import { faker } from '@faker-js/faker'; -import { createRole } from './role.js'; -import SamlAuthProvidersRoleMapping from '../../src/models/saml-auth-providers-role-mapping.ee.js'; -import { createSamlAuthProvider } from './saml-auth-provider.ee.js'; - -export const createSamlAuthProvidersRoleMapping = async (params = {}) => { - params.roleId = params.roleId || (await createRole()).id; - params.samlAuthProviderId = - params.samlAuthProviderId || (await createSamlAuthProvider()).id; - params.remoteRoleName = params.remoteRoleName || faker.person.jobType(); - - const samlAuthProvider = - await SamlAuthProvidersRoleMapping.query().insertAndFetch(params); - - return samlAuthProvider; -}; diff --git a/packages/backend/test/mocks/rest/api/v1/admin/saml-auth-providers/get-role-mappings.ee.js b/packages/backend/test/mocks/rest/api/v1/admin/saml-auth-providers/get-role-mappings.ee.js index 66fdf08a..dcd8304e 100644 --- a/packages/backend/test/mocks/rest/api/v1/admin/saml-auth-providers/get-role-mappings.ee.js +++ b/packages/backend/test/mocks/rest/api/v1/admin/saml-auth-providers/get-role-mappings.ee.js @@ -15,7 +15,7 @@ const getRoleMappingsMock = async (roleMappings) => { currentPage: null, isArray: true, totalPages: null, - type: 'SamlAuthProvidersRoleMapping', + type: 'RoleMapping', }, }; }; diff --git a/packages/backend/test/mocks/rest/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js b/packages/backend/test/mocks/rest/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js index 2ff8ca16..e921150f 100644 --- a/packages/backend/test/mocks/rest/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js +++ b/packages/backend/test/mocks/rest/api/v1/admin/saml-auth-providers/update-role-mappings.ee.js @@ -15,7 +15,7 @@ const createRoleMappingsMock = async (roleMappings) => { currentPage: null, isArray: true, totalPages: null, - type: 'SamlAuthProvidersRoleMapping', + type: 'RoleMapping', }, }; }; diff --git a/packages/web/src/pages/Authentication/RoleMappings.jsx b/packages/web/src/pages/Authentication/RoleMappings.jsx index 66a14c38..4440177b 100644 --- a/packages/web/src/pages/Authentication/RoleMappings.jsx +++ b/packages/web/src/pages/Authentication/RoleMappings.jsx @@ -66,8 +66,8 @@ function RoleMappings({ provider, providerLoading }) { const enqueueSnackbar = useEnqueueSnackbar(); const { - mutateAsync: updateSamlAuthProvidersRoleMappings, - isPending: isUpdateSamlAuthProvidersRoleMappingsPending, + mutateAsync: updateRoleMappings, + isPending: isUpdateRoleMappingsPending, } = useAdminUpdateSamlAuthProviderRoleMappings(provider?.id); const { data, isLoading: isAdminSamlAuthProviderRoleMappingsLoading } = @@ -79,7 +79,7 @@ function RoleMappings({ provider, providerLoading }) { const handleRoleMappingsUpdate = async (values) => { try { if (provider?.id) { - await updateSamlAuthProvidersRoleMappings( + await updateRoleMappings( values.roleMappings.map(({ roleId, remoteRoleName }) => ({ roleId, remoteRoleName, @@ -148,7 +148,7 @@ function RoleMappings({ provider, providerLoading }) { variant="contained" color="primary" sx={{ boxShadow: 2 }} - loading={isUpdateSamlAuthProvidersRoleMappingsPending} + loading={isUpdateRoleMappingsPending} > {formatMessage('roleMappingsForm.save')}