diff --git a/api/lib/application/users/index.js b/api/lib/application/users/index.js index ff6d48dd53f..cb73c66b3fc 100644 --- a/api/lib/application/users/index.js +++ b/api/lib/application/users/index.js @@ -129,39 +129,6 @@ const register = async function (server) { tags: ['api', 'admin', 'user', 'authentication-method'], }, }, - { - method: 'POST', - path: '/api/admin/users/{id}/remove-authentication', - config: { - pre: [ - { - method: (request, h) => - securityPreHandlers.hasAtLeastOneAccessOf([ - securityPreHandlers.checkAdminMemberHasRoleSuperAdmin, - securityPreHandlers.checkAdminMemberHasRoleSupport, - ])(request, h), - }, - ], - validate: { - params: Joi.object({ - id: identifiersType.userId, - }), - payload: Joi.object({ - data: { - attributes: { - type: Joi.string().required(), - }, - }, - }), - options: { - allowUnknown: true, - }, - }, - handler: userController.removeAuthenticationMethod, - notes: ['- Permet à un administrateur de supprimer une méthode de connexion'], - tags: ['api', 'admin', 'user'], - }, - }, ]; server.route([ diff --git a/api/lib/application/users/user-controller.js b/api/lib/application/users/user-controller.js index c361c9ca992..8ba6002b7b4 100644 --- a/api/lib/application/users/user-controller.js +++ b/api/lib/application/users/user-controller.js @@ -57,13 +57,6 @@ const resetScorecard = function (request, h, dependencies = { scorecardSerialize .then(dependencies.scorecardSerializer.serialize); }; -const removeAuthenticationMethod = async function (request, h) { - const userId = request.params.id; - const authenticationMethodType = request.payload.data.attributes.type; - await usecases.removeAuthenticationMethod({ userId, authenticationMethodType }); - return h.response().code(204); -}; - const addPixAuthenticationMethodByEmail = async function ( request, h, @@ -126,7 +119,6 @@ const userController = { reassignAuthenticationMethods, rememberUserHasSeenAssessmentInstructions, rememberUserHasSeenChallengeTooltip, - removeAuthenticationMethod, resetScorecard, }; diff --git a/api/src/identity-access-management/application/user/user.admin.controller.js b/api/src/identity-access-management/application/user/user.admin.controller.js index 8a226dd05ab..e8b750e66c7 100644 --- a/api/src/identity-access-management/application/user/user.admin.controller.js +++ b/api/src/identity-access-management/application/user/user.admin.controller.js @@ -89,11 +89,19 @@ const anonymizeUser = async function (request, h, dependencies = { userAnonymize return h.response(dependencies.userAnonymizedDetailsForAdminSerializer.serialize(anonymizedUser)).code(200); }; +const removeAuthenticationMethod = async function (request, h) { + const userId = request.params.id; + const authenticationMethodType = request.payload.data.attributes.type; + await usecases.removeAuthenticationMethod({ userId, authenticationMethodType }); + return h.response().code(204); +}; + /** * @typedef {object} UserAdminController * @property {function} anonymizeUser * @property {function} findPaginatedFilteredUsers * @property {function} getUserDetails + * @property {function} removeAuthenticationMethod * @property {function} unblockUserAccount * @property {function} updateUserDetailsByAdmin */ @@ -101,6 +109,7 @@ const userAdminController = { anonymizeUser, findPaginatedFilteredUsers, getUserDetails, + removeAuthenticationMethod, unblockUserAccount, updateUserDetailsByAdmin, }; diff --git a/api/src/identity-access-management/application/user/user.admin.route.js b/api/src/identity-access-management/application/user/user.admin.route.js index 3eca849f58e..7fbe9df5c19 100644 --- a/api/src/identity-access-management/application/user/user.admin.route.js +++ b/api/src/identity-access-management/application/user/user.admin.route.js @@ -180,4 +180,37 @@ export const userAdminRoutes = [ tags: ['api', 'admin', 'identity-access-management', 'user'], }, }, + { + method: 'POST', + path: '/api/admin/users/{id}/remove-authentication', + config: { + pre: [ + { + method: (request, h) => + securityPreHandlers.hasAtLeastOneAccessOf([ + securityPreHandlers.checkAdminMemberHasRoleSuperAdmin, + securityPreHandlers.checkAdminMemberHasRoleSupport, + ])(request, h), + }, + ], + validate: { + params: Joi.object({ + id: identifiersType.userId, + }), + payload: Joi.object({ + data: { + attributes: { + type: Joi.string().required(), + }, + }, + }), + options: { + allowUnknown: true, + }, + }, + handler: (request, h) => userAdminController.removeAuthenticationMethod(request, h), + notes: ['- Permet à un administrateur de supprimer une méthode de connexion'], + tags: ['api', 'identity-access-management', 'admin', 'user'], + }, + }, ]; diff --git a/api/lib/domain/usecases/remove-authentication-method.js b/api/src/identity-access-management/domain/usecases/remove-authentication-method.usecase.js similarity index 77% rename from api/lib/domain/usecases/remove-authentication-method.js rename to api/src/identity-access-management/domain/usecases/remove-authentication-method.usecase.js index 5e5f02f343b..0d3446bb49d 100644 --- a/api/lib/domain/usecases/remove-authentication-method.js +++ b/api/src/identity-access-management/domain/usecases/remove-authentication-method.usecase.js @@ -1,7 +1,16 @@ -import { NON_OIDC_IDENTITY_PROVIDERS } from '../../../src/identity-access-management/domain/constants/identity-providers.js'; -import * as OidcIdentityProviders from '../../../src/identity-access-management/domain/constants/oidc-identity-providers.js'; -import { UserNotAuthorizedToRemoveAuthenticationMethod } from '../../../src/shared/domain/errors.js'; +import { UserNotAuthorizedToRemoveAuthenticationMethod } from '../../../shared/domain/errors.js'; +import { NON_OIDC_IDENTITY_PROVIDERS } from '../constants/identity-providers.js'; +import * as OidcIdentityProviders from '../constants/oidc-identity-providers.js'; +/** + * @param{object} params + * @param{string} params.userId + * @param{string} params.authenticationMethodType + * @param{UserRepository} userRepository + * @param{AuthenticationMethodRepository} authenticationMethodRepository + * @returns {Promise} + * @throws UserNotAuthorizedToRemoveAuthenticationMethod + */ export const removeAuthenticationMethod = async function ({ userId, authenticationMethodType, diff --git a/api/tests/acceptance/application/users/users-controller-remove-authentication-method_test.js b/api/tests/acceptance/application/users/users-controller-remove-authentication-method_test.js deleted file mode 100644 index 0447098cebc..00000000000 --- a/api/tests/acceptance/application/users/users-controller-remove-authentication-method_test.js +++ /dev/null @@ -1,69 +0,0 @@ -import { NON_OIDC_IDENTITY_PROVIDERS } from '../../../../src/identity-access-management/domain/constants/identity-providers.js'; -import { - createServer, - databaseBuilder, - expect, - generateValidRequestAuthorizationHeader, - insertUserWithRoleSuperAdmin, - knex, -} from '../../../test-helper.js'; - -describe('Acceptance | Controller | users-controller-remove-authentication-method', function () { - let server; - let user; - let options; - - beforeEach(async function () { - server = await createServer(); - user = databaseBuilder.factory.buildUser({ username: 'jhn.doe0101', email: null }); - databaseBuilder.factory.buildAuthenticationMethod.withPixAsIdentityProviderAndHashedPassword({ - userId: user.id, - }); - databaseBuilder.factory.buildAuthenticationMethod.withGarAsIdentityProvider({ userId: user.id }); - - const superAdmin = await insertUserWithRoleSuperAdmin(); - options = { - method: 'POST', - url: `/api/admin/users/${user.id}/remove-authentication`, - payload: { - data: { - attributes: { - type: 'USERNAME', - }, - }, - }, - headers: { authorization: generateValidRequestAuthorizationHeader(superAdmin.id) }, - }; - return databaseBuilder.commit(); - }); - - describe('POST /admin/users/:id/remove-authentication', function () { - it('should return 204', async function () { - // when - const response = await server.inject(options); - - // then - expect(response.statusCode).to.equal(204); - }); - - it('should set the username to null', async function () { - // when - await server.inject(options); - - // then - const updatedUser = await knex('users').where({ id: user.id }).first(); - expect(updatedUser.username).to.be.null; - }); - - it('should remove PIX authenticationMethod', async function () { - // when - await server.inject(options); - - // then - const pixAuthenticationMethod = await knex('authentication-methods') - .where({ userId: user.id, identityProvider: NON_OIDC_IDENTITY_PROVIDERS.PIX.code }) - .first(); - expect(pixAuthenticationMethod).to.be.undefined; - }); - }); -}); diff --git a/api/tests/identity-access-management/acceptance/application/user/user.admin.route.test.js b/api/tests/identity-access-management/acceptance/application/user/user.admin.route.test.js index bc140d7a782..4e30c1fc9e5 100644 --- a/api/tests/identity-access-management/acceptance/application/user/user.admin.route.test.js +++ b/api/tests/identity-access-management/acceptance/application/user/user.admin.route.test.js @@ -1,3 +1,4 @@ +import { NON_OIDC_IDENTITY_PROVIDERS } from '../../../../../src/identity-access-management/domain/constants/identity-providers.js'; import { QUERY_TYPES } from '../../../../../src/identity-access-management/domain/constants/user-query.js'; import { createServer, @@ -412,4 +413,64 @@ describe('Acceptance | Identity Access Management | Application | Route | Admin expect(organizationLearnerMembership.disabledAt).not.to.be.null; }); }); + + describe('POST /api/admin/users/{id}/remove-authentication', function () { + let server; + let user; + let options; + + beforeEach(async function () { + server = await createServer(); + user = databaseBuilder.factory.buildUser({ username: 'jhn.doe0101', email: null }); + databaseBuilder.factory.buildAuthenticationMethod.withPixAsIdentityProviderAndHashedPassword({ + userId: user.id, + }); + databaseBuilder.factory.buildAuthenticationMethod.withGarAsIdentityProvider({ userId: user.id }); + + const superAdmin = await insertUserWithRoleSuperAdmin(); + options = { + method: 'POST', + url: `/api/admin/users/${user.id}/remove-authentication`, + payload: { + data: { + attributes: { + type: 'USERNAME', + }, + }, + }, + headers: { authorization: generateValidRequestAuthorizationHeader(superAdmin.id) }, + }; + return databaseBuilder.commit(); + }); + + describe('POST /admin/users/:id/remove-authentication', function () { + it('returns a 204 HTTP status code', async function () { + // when + const response = await server.inject(options); + + // then + expect(response.statusCode).to.equal(204); + }); + + it('sets the username to null', async function () { + // when + await server.inject(options); + + // then + const updatedUser = await knex('users').where({ id: user.id }).first(); + expect(updatedUser.username).to.be.null; + }); + + it('removes PIX authenticationMethod', async function () { + // when + await server.inject(options); + + // then + const pixAuthenticationMethod = await knex('authentication-methods') + .where({ userId: user.id, identityProvider: NON_OIDC_IDENTITY_PROVIDERS.PIX.code }) + .first(); + expect(pixAuthenticationMethod).to.be.undefined; + }); + }); + }); }); diff --git a/api/tests/identity-access-management/integration/application/user/user.admin.route.test.js b/api/tests/identity-access-management/integration/application/user/user.admin.route.test.js index caaeacc3f86..537210d7f59 100644 --- a/api/tests/identity-access-management/integration/application/user/user.admin.route.test.js +++ b/api/tests/identity-access-management/integration/application/user/user.admin.route.test.js @@ -1,9 +1,15 @@ import { identityAccessManagementRoutes } from '../../../../../src/identity-access-management/application/routes.js'; import { userAdminController } from '../../../../../src/identity-access-management/application/user/user.admin.controller.js'; +import { NON_OIDC_IDENTITY_PROVIDERS } from '../../../../../src/identity-access-management/domain/constants/identity-providers.js'; +import * as OidcIdentityProviders from '../../../../../src/identity-access-management/domain/constants/oidc-identity-providers.js'; import { QUERY_TYPES } from '../../../../../src/identity-access-management/domain/constants/user-query.js'; import { securityPreHandlers } from '../../../../../src/shared/application/security-pre-handlers.js'; import { expect, HttpTestServer, sinon } from '../../../../test-helper.js'; +const CODE_IDENTITY_PROVIDER_GAR = NON_OIDC_IDENTITY_PROVIDERS.GAR.code; +const CODE_IDENTITY_PROVIDER_POLE_EMPLOI = OidcIdentityProviders.POLE_EMPLOI.code; + +const oidcProviderCode = 'genericOidcProviderCode'; const routesUnderTest = identityAccessManagementRoutes[0]; describe('Integration | Identity Access Management | Application | Route | Admin | User', function () { @@ -267,4 +273,101 @@ describe('Integration | Identity Access Management | Application | Route | Admin sinon.assert.notCalled(userAdminController.anonymizeUser); }); }); + + describe('POST /api/admin/users/{id}/remove-authentication', function () { + // eslint-disable-next-line mocha/no-setup-in-describe + [CODE_IDENTITY_PROVIDER_GAR, 'EMAIL', 'USERNAME', CODE_IDENTITY_PROVIDER_POLE_EMPLOI, oidcProviderCode].forEach( + (type) => { + it(`returns 200 when user is "SUPER_ADMIN" and type is ${type}`, async function () { + // given + sinon.stub(userAdminController, 'removeAuthenticationMethod').returns('ok'); + sinon + .stub(securityPreHandlers, 'checkAdminMemberHasRoleSuperAdmin') + .callsFake((request, h) => h.response(true)); + sinon + .stub(securityPreHandlers, 'checkAdminMemberHasRoleSupport') + .callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403)); + + // when + const result = await httpTestServer.request('POST', '/api/admin/users/1/remove-authentication', { + data: { + attributes: { + type, + }, + }, + }); + + // then + expect(result.statusCode).to.equal(200); + sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSuperAdmin); + sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSupport); + sinon.assert.calledOnce(userAdminController.removeAuthenticationMethod); + }); + + it(`returns 200 when user is "SUPPORT" and type is ${type}`, async function () { + // given + sinon.stub(userAdminController, 'removeAuthenticationMethod').returns('ok'); + sinon + .stub(securityPreHandlers, 'checkAdminMemberHasRoleSuperAdmin') + .callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403)); + sinon.stub(securityPreHandlers, 'checkAdminMemberHasRoleSupport').callsFake((request, h) => h.response(true)); + + // when + const result = await httpTestServer.request('POST', '/api/admin/users/1/remove-authentication', { + data: { + attributes: { + type, + }, + }, + }); + + // then + expect(result.statusCode).to.equal(200); + sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSuperAdmin); + sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSupport); + sinon.assert.calledOnce(userAdminController.removeAuthenticationMethod); + }); + }, + ); + + it('returns 400 when id is not a number', async function () { + // when + const result = await httpTestServer.request('POST', '/api/admin/users/invalid-id/remove-authentication', { + data: { + attributes: { + type: 'EMAIL', + }, + }, + }); + + // then + expect(result.statusCode).to.equal(400); + }); + + it(`returns 403 when user don't have access (CERTIF | METIER)`, async function () { + // given + sinon.stub(userAdminController, 'removeAuthenticationMethod').returns('ok'); + sinon + .stub(securityPreHandlers, 'checkAdminMemberHasRoleSuperAdmin') + .callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403)); + sinon + .stub(securityPreHandlers, 'checkAdminMemberHasRoleSupport') + .callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403)); + + // when + const result = await httpTestServer.request('POST', '/api/admin/users/1/remove-authentication', { + data: { + attributes: { + type: OidcIdentityProviders.POLE_EMPLOI.code, + }, + }, + }); + + // then + sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSuperAdmin); + sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSupport); + sinon.assert.notCalled(userAdminController.removeAuthenticationMethod); + expect(result.statusCode).to.equal(403); + }); + }); }); diff --git a/api/tests/identity-access-management/unit/application/user/user.admin.controller.test.js b/api/tests/identity-access-management/unit/application/user/user.admin.controller.test.js index fb71df5ebff..3e8d24dd79c 100644 --- a/api/tests/identity-access-management/unit/application/user/user.admin.controller.test.js +++ b/api/tests/identity-access-management/unit/application/user/user.admin.controller.test.js @@ -3,7 +3,8 @@ import { QUERY_TYPES } from '../../../../../src/identity-access-management/domai import { User } from '../../../../../src/identity-access-management/domain/models/User.js'; import { usecases } from '../../../../../src/identity-access-management/domain/usecases/index.js'; import { DomainTransaction } from '../../../../../src/shared/domain/DomainTransaction.js'; -import { expect, hFake, sinon } from '../../../../test-helper.js'; +import { UserNotAuthorizedToRemoveAuthenticationMethod } from '../../../../../src/shared/domain/errors.js'; +import { catchErr, expect, hFake, sinon } from '../../../../test-helper.js'; describe('Unit | Identity Access Management | Application | Controller | Admin | User', function () { describe('#findPaginatedFilteredUsers', function () { @@ -238,4 +239,46 @@ describe('Unit | Identity Access Management | Application | Controller | Admin | expect(response.source).to.deep.equal(anonymizedUserSerialized); }); }); + + describe('#removeAuthenticationMethod', function () { + let removeAuthenticationMethodStub, request; + + beforeEach(function () { + removeAuthenticationMethodStub = sinon.stub(usecases, 'removeAuthenticationMethod'); + request = { + params: { id: 123 }, + payload: { + data: { + attributes: { + type: 'EMAIL', + }, + }, + }, + }; + }); + + context('Success cases', function () { + it('returns a 204 HTTP status code', async function () { + // given + removeAuthenticationMethodStub.resolves(); + + // when + const response = await userAdminController.removeAuthenticationMethod(request, hFake); + + // then + expect(response.statusCode).to.equal(204); + }); + }); + context('Error cases', function () { + it('throws a UserNotAuthorizedToRemoveAuthenticationMethod when usecase has thrown this error', async function () { + // given + removeAuthenticationMethodStub.throws(new UserNotAuthorizedToRemoveAuthenticationMethod()); + // when + const error = await catchErr(userAdminController.removeAuthenticationMethod)(request, hFake); + + // then + expect(error).to.be.instanceOf(UserNotAuthorizedToRemoveAuthenticationMethod); + }); + }); + }); }); diff --git a/api/tests/unit/domain/usecases/remove-authentication-method_test.js b/api/tests/identity-access-management/unit/domain/usecases/remove-authentication-method.usecase.test.js similarity index 91% rename from api/tests/unit/domain/usecases/remove-authentication-method_test.js rename to api/tests/identity-access-management/unit/domain/usecases/remove-authentication-method.usecase.test.js index 7d765c81835..499151e819a 100644 --- a/api/tests/unit/domain/usecases/remove-authentication-method_test.js +++ b/api/tests/identity-access-management/unit/domain/usecases/remove-authentication-method.usecase.test.js @@ -1,10 +1,10 @@ -import { removeAuthenticationMethod } from '../../../../lib/domain/usecases/remove-authentication-method.js'; -import { NON_OIDC_IDENTITY_PROVIDERS } from '../../../../src/identity-access-management/domain/constants/identity-providers.js'; -import * as OidcIdentityProviders from '../../../../src/identity-access-management/domain/constants/oidc-identity-providers.js'; -import { UserNotAuthorizedToRemoveAuthenticationMethod } from '../../../../src/shared/domain/errors.js'; -import { catchErr, domainBuilder, expect, sinon } from '../../../test-helper.js'; +import { NON_OIDC_IDENTITY_PROVIDERS } from '../../../../../src/identity-access-management/domain/constants/identity-providers.js'; +import * as OidcIdentityProviders from '../../../../../src/identity-access-management/domain/constants/oidc-identity-providers.js'; +import { removeAuthenticationMethod } from '../../../../../src/identity-access-management/domain/usecases/remove-authentication-method.usecase.js'; +import { UserNotAuthorizedToRemoveAuthenticationMethod } from '../../../../../src/shared/domain/errors.js'; +import { catchErr, domainBuilder, expect, sinon } from '../../../../test-helper.js'; -describe('Unit | UseCase | remove-authentication-method', function () { +describe('Unit | Identity Access Management | Domain | UseCase | remove-authentication-method', function () { let userRepository; let authenticationMethodRepository; @@ -47,7 +47,7 @@ describe('Unit | UseCase | remove-authentication-method', function () { context('When authentication method type is EMAIL', function () { const authenticationMethodType = 'EMAIL'; - it('should set the email to null', async function () { + it('sets the email to null', async function () { // given const user = domainBuilder.buildUser(); userRepository.get.resolves(user); @@ -91,7 +91,7 @@ describe('Unit | UseCase | remove-authentication-method', function () { }); context('When user has a username', function () { - it('should not remove PIX authentication method', async function () { + it('does not remove PIX authentication method', async function () { // given const user = domainBuilder.buildUser({ username: 'john.doe0101' }); userRepository.get.resolves(user); @@ -115,7 +115,7 @@ describe('Unit | UseCase | remove-authentication-method', function () { context('When authentication method type is USERNAME', function () { const authenticationMethodType = 'USERNAME'; - it('should set the username to null', async function () { + it('sets the username to null', async function () { // given const user = domainBuilder.buildUser(); userRepository.get.resolves(user); @@ -159,7 +159,7 @@ describe('Unit | UseCase | remove-authentication-method', function () { }); context('When user has an email', function () { - it('should not remove PIX authentication method', async function () { + it('does not remove PIX authentication method', async function () { // given const user = domainBuilder.buildUser({ email: 'john.doe@example.net' }); userRepository.get.resolves(user); @@ -256,7 +256,7 @@ describe('Unit | UseCase | remove-authentication-method', function () { }); }); context('When there is only one remaining authentication method', function () { - it('should throw a UserNotAuthorizedToRemoveAuthenticationMethod', async function () { + it('throws a UserNotAuthorizedToRemoveAuthenticationMethod', async function () { // given const user = domainBuilder.buildUser(); userRepository.get.resolves(user); @@ -277,7 +277,7 @@ describe('Unit | UseCase | remove-authentication-method', function () { expect(error).to.be.an.instanceOf(UserNotAuthorizedToRemoveAuthenticationMethod); }); - it('should not remove the authentication method', async function () { + it('does not remove the authentication method', async function () { // given const user = domainBuilder.buildUser(); userRepository.get.resolves(user); diff --git a/api/tests/integration/application/users/user-controller_test.js b/api/tests/integration/application/users/user-controller_test.js deleted file mode 100644 index 08de8ff3585..00000000000 --- a/api/tests/integration/application/users/user-controller_test.js +++ /dev/null @@ -1,76 +0,0 @@ -import * as moduleUnderTest from '../../../../lib/application/users/index.js'; -import { usecases } from '../../../../lib/domain/usecases/index.js'; -import { securityPreHandlers } from '../../../../src/shared/application/security-pre-handlers.js'; -import { UserNotAuthorizedToRemoveAuthenticationMethod } from '../../../../src/shared/domain/errors.js'; -import { expect, HttpTestServer, sinon } from '../../../test-helper.js'; - -describe('Integration | Application | Users | user-controller', function () { - let sandbox; - let httpTestServer; - - beforeEach(async function () { - sandbox = sinon.createSandbox(); - sandbox.stub(securityPreHandlers, 'hasAtLeastOneAccessOf'); - - sandbox.stub(usecases, 'removeAuthenticationMethod'); - - httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); - }); - - afterEach(function () { - sandbox.restore(); - }); - - describe('#removeAuthenticationMethod', function () { - const method = 'POST'; - const url = '/api/admin/users/1/remove-authentication'; - const payload = { - data: { - attributes: { - type: 'EMAIL', - }, - }, - }; - - beforeEach(function () { - securityPreHandlers.hasAtLeastOneAccessOf.returns(() => true); - }); - - context('Success cases', function () { - it('should return a HTTP response with status code 204', async function () { - // given - usecases.removeAuthenticationMethod.resolves(); - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(204); - }); - }); - - context('Error cases', function () { - it('should return a 403 HTTP response when when user is not allowed to access resource', async function () { - // given - securityPreHandlers.hasAtLeastOneAccessOf.returns((request, h) => h.response().code(403).takeover()); - - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(403); - }); - - it('should return a 403 HTTP response when the usecase throw a UserNotAuthorizedToRemoveAuthenticationMethod', async function () { - // given - usecases.removeAuthenticationMethod.throws(new UserNotAuthorizedToRemoveAuthenticationMethod()); - // when - const response = await httpTestServer.request(method, url, payload); - - // then - expect(response.statusCode).to.equal(403); - }); - }); - }); -}); diff --git a/api/tests/unit/application/users/index_test.js b/api/tests/unit/application/users/index_test.js index e8fd40db54e..800c52d2cec 100644 --- a/api/tests/unit/application/users/index_test.js +++ b/api/tests/unit/application/users/index_test.js @@ -150,115 +150,6 @@ describe('Unit | Router | user-router', function () { }); }); - describe('POST /api/admin/users/{id}/remove-authentication', function () { - // eslint-disable-next-line mocha/no-setup-in-describe - [CODE_IDENTITY_PROVIDER_GAR, 'EMAIL', 'USERNAME', CODE_IDENTITY_PROVIDER_POLE_EMPLOI, oidcProviderCode].forEach( - (type) => { - it(`returns 200 when user is "SUPER_ADMIN" and type is ${type}`, async function () { - // given - sinon.stub(userController, 'removeAuthenticationMethod').returns('ok'); - sinon - .stub(securityPreHandlers, 'checkAdminMemberHasRoleSuperAdmin') - .callsFake((request, h) => h.response(true)); - sinon - .stub(securityPreHandlers, 'checkAdminMemberHasRoleSupport') - .callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403)); - const httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); - - // when - const result = await httpTestServer.request('POST', '/api/admin/users/1/remove-authentication', { - data: { - attributes: { - type, - }, - }, - }); - - // then - expect(result.statusCode).to.equal(200); - sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSuperAdmin); - sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSupport); - sinon.assert.calledOnce(userController.removeAuthenticationMethod); - }); - - it(`returns 200 when user is "SUPPORT" and type is ${type}`, async function () { - // given - sinon.stub(userController, 'removeAuthenticationMethod').returns('ok'); - sinon - .stub(securityPreHandlers, 'checkAdminMemberHasRoleSuperAdmin') - .callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403)); - sinon - .stub(securityPreHandlers, 'checkAdminMemberHasRoleSupport') - .callsFake((request, h) => h.response(true)); - const httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); - - // when - const result = await httpTestServer.request('POST', '/api/admin/users/1/remove-authentication', { - data: { - attributes: { - type, - }, - }, - }); - - // then - expect(result.statusCode).to.equal(200); - sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSuperAdmin); - sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSupport); - sinon.assert.calledOnce(userController.removeAuthenticationMethod); - }); - }, - ); - - it('returns 400 when id is not a number', async function () { - // given - const httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); - - // when - const result = await httpTestServer.request('POST', '/api/admin/users/invalid-id/remove-authentication', { - data: { - attributes: { - type: 'EMAIL', - }, - }, - }); - - // then - expect(result.statusCode).to.equal(400); - }); - - it(`should return 403 when user don't have access (CERTIF | METIER)`, async function () { - // given - sinon.stub(userController, 'removeAuthenticationMethod').returns('ok'); - sinon - .stub(securityPreHandlers, 'checkAdminMemberHasRoleSuperAdmin') - .callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403)); - sinon - .stub(securityPreHandlers, 'checkAdminMemberHasRoleSupport') - .callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403)); - const httpTestServer = new HttpTestServer(); - await httpTestServer.register(moduleUnderTest); - - // when - const result = await httpTestServer.request('POST', '/api/admin/users/1/remove-authentication', { - data: { - attributes: { - type: OidcIdentityProviders.POLE_EMPLOI.code, - }, - }, - }); - - // then - sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSuperAdmin); - sinon.assert.calledOnce(securityPreHandlers.checkAdminMemberHasRoleSupport); - sinon.assert.notCalled(userController.removeAuthenticationMethod); - expect(result.statusCode).to.equal(403); - }); - }); - describe('POST /api/admin/users/{userId}/authentication-methods/{authenticationMethodId}', function () { // eslint-disable-next-line mocha/no-setup-in-describe [CODE_IDENTITY_PROVIDER_GAR, CODE_IDENTITY_PROVIDER_POLE_EMPLOI, oidcProviderCode].forEach((identityProvider) => {