Skip to content

Commit 51ae5e6

Browse files
[TECH] Migrer la route POST api/admin/memberships/{id}/disable
#11423
2 parents 9f9eb0f + 0a73d9e commit 51ae5e6

File tree

10 files changed

+150
-96
lines changed

10 files changed

+150
-96
lines changed

Diff for: api/lib/application/memberships/index.js

+3-34
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,11 @@ import Joi from 'joi';
22

33
import { securityPreHandlers } from '../../../src/shared/application/security-pre-handlers.js';
44
import { identifiersType } from '../../../src/shared/domain/types/identifiers-type.js';
5-
import { membershipController } from './membership-controller.js';
5+
import { membershipController } from '../../../src/team/application/membership/membership.controller.js';
6+
import { membershipController as libMembershipController } from './membership-controller.js';
67

78
const register = async function (server) {
8-
const adminRoutes = [
9-
{
10-
method: 'POST',
11-
path: '/api/admin/memberships/{id}/disable',
12-
config: {
13-
pre: [
14-
{
15-
method: (request, h) =>
16-
securityPreHandlers.hasAtLeastOneAccessOf([
17-
securityPreHandlers.checkAdminMemberHasRoleSuperAdmin,
18-
securityPreHandlers.checkAdminMemberHasRoleSupport,
19-
securityPreHandlers.checkAdminMemberHasRoleMetier,
20-
])(request, h),
21-
assign: 'hasAuthorizationToAccessAdminScope',
22-
},
23-
],
24-
validate: {
25-
params: Joi.object({
26-
id: identifiersType.membershipId,
27-
}),
28-
},
29-
handler: membershipController.disable,
30-
tags: ['api'],
31-
notes: [
32-
"- **Cette route est restreinte aux utilisateurs authentifiés ayant les droits d'accès**\n" +
33-
"- Elle permet la désactivation d'un membre",
34-
],
35-
},
36-
},
37-
];
38-
399
server.route([
40-
...adminRoutes,
4110
{
4211
method: 'POST',
4312
path: '/api/memberships/me/disable',
@@ -57,7 +26,7 @@ const register = async function (server) {
5726
organizationId: identifiersType.organizationId,
5827
}),
5928
},
60-
handler: membershipController.disableOwnOrganizationMembership,
29+
handler: libMembershipController.disableOwnOrganizationMembership,
6130
tags: ['api'],
6231
notes: [
6332
"- **Cette route est restreinte aux utilisateurs authentifiés en tant qu'administrateur de l'organisation\n" +
+1-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import * as requestResponseUtils from '../../../src/shared/infrastructure/utils/request-response-utils.js';
22
import { usecases } from '../../domain/usecases/index.js';
33

4-
const disable = async function (request, h) {
5-
const membershipId = request.params.id;
6-
const userId = requestResponseUtils.extractUserIdFromRequest(request);
7-
8-
await usecases.disableMembership({ membershipId, userId });
9-
return h.response().code(204);
10-
};
11-
124
const disableOwnOrganizationMembership = async function (request, h) {
135
const organizationId = request.payload.organizationId;
146
const userId = requestResponseUtils.extractUserIdFromRequest(request);
@@ -18,6 +10,6 @@ const disableOwnOrganizationMembership = async function (request, h) {
1810
return h.response().code(204);
1911
};
2012

21-
const membershipController = { disable, disableOwnOrganizationMembership };
13+
const membershipController = { disableOwnOrganizationMembership };
2214

2315
export { membershipController };

Diff for: api/src/team/application/membership/membership.admin.route.js

+28
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,32 @@ export const membershipAdminRoutes = [
139139
],
140140
},
141141
},
142+
{
143+
method: 'POST',
144+
path: '/api/admin/memberships/{id}/disable',
145+
config: {
146+
pre: [
147+
{
148+
method: (request, h) =>
149+
securityPreHandlers.hasAtLeastOneAccessOf([
150+
securityPreHandlers.checkAdminMemberHasRoleSuperAdmin,
151+
securityPreHandlers.checkAdminMemberHasRoleSupport,
152+
securityPreHandlers.checkAdminMemberHasRoleMetier,
153+
])(request, h),
154+
assign: 'hasAuthorizationToAccessAdminScope',
155+
},
156+
],
157+
validate: {
158+
params: Joi.object({
159+
id: identifiersType.membershipId,
160+
}),
161+
},
162+
handler: (request, h) => membershipController.disable(request, h),
163+
tags: ['api'],
164+
notes: [
165+
"- **Cette route est restreinte aux utilisateurs authentifiés ayant les droits d'accès**\n" +
166+
"- Elle permet la désactivation d'un membre",
167+
],
168+
},
169+
},
142170
];

Diff for: api/src/team/application/membership/membership.controller.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ const create = async function (request, h, dependencies = { membershipSerializer
1313
return h.response(dependencies.membershipSerializer.serializeForAdmin(membership)).created();
1414
};
1515

16+
const disable = async function (request, h) {
17+
const membershipId = request.params.id;
18+
const userId = requestResponseUtils.extractUserIdFromRequest(request);
19+
20+
await usecases.disableMembership({ membershipId, userId });
21+
return h.response().code(204);
22+
};
23+
1624
const update = async function (request, h, dependencies = { requestResponseUtils, membershipSerializer }) {
1725
const membershipId = request.params.id;
1826
const userId = dependencies.requestResponseUtils.extractUserIdFromRequest(request);
@@ -42,6 +50,6 @@ const findPaginatedFilteredMemberships = async function (request) {
4250
return membershipSerializer.serialize(memberships, pagination);
4351
};
4452

45-
const membershipController = { create, findPaginatedFilteredMemberships, update };
53+
const membershipController = { create, disable, findPaginatedFilteredMemberships, update };
4654

4755
export { membershipController };

Diff for: api/tests/integration/application/memberships/membership-controller_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as moduleUnderTest from '../../../../lib/application/memberships/index.js';
2-
import { usecases } from '../../../../lib/domain/usecases/index.js';
32
import { securityPreHandlers } from '../../../../src/shared/application/security-pre-handlers.js';
3+
import { usecases } from '../../../../src/team/domain/usecases/index.js';
44
import { domainBuilder, expect, HttpTestServer, sinon } from '../../../test-helper.js';
55

66
describe('Integration | Application | Memberships | membership-controller', function () {

Diff for: api/tests/team/acceptance/application/membership/membership.admin.route.test.js

+57
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,63 @@ describe('Acceptance | Team | Admin | Route | membership', function () {
266266
});
267267
});
268268

269+
describe('POST /api/admin/memberships/{id}/disable', function () {
270+
let options;
271+
let adminUserId;
272+
let userId;
273+
let organizationId;
274+
let membershipId;
275+
276+
beforeEach(async function () {
277+
const externalId = 'externalId';
278+
adminUserId = databaseBuilder.factory.buildUser.withRole().id;
279+
organizationId = databaseBuilder.factory.buildOrganization({ externalId, type: 'SCO' }).id;
280+
userId = databaseBuilder.factory.buildUser().id;
281+
membershipId = databaseBuilder.factory.buildMembership({
282+
organizationId,
283+
userId,
284+
organizationRole: Membership.roles.MEMBER,
285+
}).id;
286+
databaseBuilder.factory.buildCertificationCenter({ externalId });
287+
288+
await databaseBuilder.commit();
289+
options = {
290+
method: 'POST',
291+
url: `/api/admin/memberships/${membershipId}/disable`,
292+
payload: {
293+
data: {
294+
id: membershipId.toString(),
295+
type: 'memberships',
296+
relationships: {
297+
user: {
298+
data: {
299+
type: 'users',
300+
id: userId,
301+
},
302+
},
303+
organization: {
304+
data: {
305+
type: 'organizations',
306+
id: organizationId,
307+
},
308+
},
309+
},
310+
},
311+
},
312+
headers: generateAuthenticatedUserRequestHeaders({ userId: adminUserId }),
313+
};
314+
});
315+
316+
it('returns 204 if user is Pix Admin', async function () {
317+
// given
318+
// when
319+
const response = await server.inject(options);
320+
321+
// then
322+
expect(response.statusCode).to.equal(204);
323+
});
324+
});
325+
269326
describe('GET /api/admin/organizations/{id}/memberships', function () {
270327
it('returns the matching membership as JSON API', async function () {
271328
// given

Diff for: api/tests/team/unit/application/membership/membership.admin.route.test.js

+46-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { membershipController } from '../../../../../src/team/application/member
33
import { teamRoutes } from '../../../../../src/team/application/routes.js';
44
import { expect, HttpTestServer, sinon } from '../../../../test-helper.js';
55

6-
describe('Unit | Team | Application | Route | Membership', function () {
6+
describe('Unit | Team | Application | Admin | Route | Membership', function () {
77
describe('POST /api/admin/memberships', function () {
88
it('returns forbidden access if admin member has CERTIF role', async function () {
99
// given
@@ -70,4 +70,49 @@ describe('Unit | Team | Application | Route | Membership', function () {
7070
expect(response.statusCode).to.equal(403);
7171
});
7272
});
73+
74+
describe('POST /api/admin/memberships/{id}/disable', function () {
75+
it('returns 204 if user is Pix Admin', async function () {
76+
// given
77+
sinon.stub(securityPreHandlers, 'hasAtLeastOneAccessOf').returns(() => true);
78+
sinon.stub(membershipController, 'disable').callsFake((request, h) => h.response().code(204));
79+
80+
const httpTestServer = new HttpTestServer();
81+
await httpTestServer.register(teamRoutes);
82+
const membershipId = 123;
83+
84+
// when
85+
const response = await httpTestServer.request('POST', `/api/admin/memberships/${membershipId}/disable`);
86+
87+
// then
88+
expect(response.statusCode).to.equal(204);
89+
expect(membershipController.disable).to.have.been.called;
90+
});
91+
92+
it('returns forbidden access if admin member has CERTIF role', async function () {
93+
// given
94+
sinon.stub(membershipController, 'disable');
95+
96+
sinon.stub(securityPreHandlers, 'checkAdminMemberHasRoleCertif').callsFake((request, h) => h.response(true));
97+
sinon
98+
.stub(securityPreHandlers, 'checkAdminMemberHasRoleSuperAdmin')
99+
.callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403));
100+
sinon
101+
.stub(securityPreHandlers, 'checkAdminMemberHasRoleSupport')
102+
.callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403));
103+
sinon
104+
.stub(securityPreHandlers, 'checkAdminMemberHasRoleMetier')
105+
.callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403));
106+
107+
const httpTestServer = new HttpTestServer();
108+
await httpTestServer.register(teamRoutes);
109+
110+
// when
111+
const response = await httpTestServer.request('POST', '/api/admin/memberships/1/disable');
112+
113+
// then
114+
expect(response.statusCode).to.equal(403);
115+
expect(membershipController.disable).to.have.not.been.called;
116+
});
117+
});
73118
});

Diff for: api/tests/unit/domain/usecases/disable-membership_test.js renamed to api/tests/team/unit/domain/usecases/disable-membership.usecase.test.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { disableMembership } from '../../../../lib/domain/usecases/disable-membership.js';
2-
import { MembershipUpdateError } from '../../../../src/shared/domain/errors.js';
3-
import { catchErr, expect, sinon } from '../../../test-helper.js';
1+
import { MembershipUpdateError } from '../../../../../src/shared/domain/errors.js';
2+
import { disableMembership } from '../../../../../src/team/domain/usecases/disable-membership.usecase.js';
3+
import { catchErr, expect, sinon } from '../../../../test-helper.js';
44

5-
describe('Unit | UseCase | disable-membership', function () {
5+
describe('Unit | Team | Domain | UseCase | disable-membership', function () {
66
let membershipRepository;
77

88
beforeEach(function () {

Diff for: api/tests/unit/application/memberships/index_test.js

+1-46
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,9 @@
11
import * as moduleUnderTest from '../../../../lib/application/memberships/index.js';
2-
import { membershipController } from '../../../../lib/application/memberships/membership-controller.js';
32
import { securityPreHandlers } from '../../../../src/shared/application/security-pre-handlers.js';
3+
import { membershipController } from '../../../../src/team/application/membership/membership.controller.js';
44
import { expect, HttpTestServer, sinon } from '../../../test-helper.js';
55

66
describe('Unit | Router | membership-router', function () {
7-
describe('POST /api/admin/memberships/{id}/disable', function () {
8-
it('should return 204 if user is Pix Admin', async function () {
9-
// given
10-
sinon.stub(securityPreHandlers, 'hasAtLeastOneAccessOf').returns(() => true);
11-
sinon.stub(membershipController, 'disable').callsFake((request, h) => h.response().code(204));
12-
13-
const httpTestServer = new HttpTestServer();
14-
await httpTestServer.register(moduleUnderTest);
15-
const membershipId = 123;
16-
17-
// when
18-
const response = await httpTestServer.request('POST', `/api/admin/memberships/${membershipId}/disable`);
19-
20-
// then
21-
expect(response.statusCode).to.equal(204);
22-
expect(membershipController.disable).to.have.been.called;
23-
});
24-
25-
it('returns forbidden access if admin member has CERTIF role', async function () {
26-
// given
27-
sinon.stub(membershipController, 'disable');
28-
29-
sinon.stub(securityPreHandlers, 'checkAdminMemberHasRoleCertif').callsFake((request, h) => h.response(true));
30-
sinon
31-
.stub(securityPreHandlers, 'checkAdminMemberHasRoleSuperAdmin')
32-
.callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403));
33-
sinon
34-
.stub(securityPreHandlers, 'checkAdminMemberHasRoleSupport')
35-
.callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403));
36-
sinon
37-
.stub(securityPreHandlers, 'checkAdminMemberHasRoleMetier')
38-
.callsFake((request, h) => h.response({ errors: new Error('forbidden') }).code(403));
39-
40-
const httpTestServer = new HttpTestServer();
41-
await httpTestServer.register(moduleUnderTest);
42-
43-
// when
44-
const response = await httpTestServer.request('POST', '/api/admin/memberships/1/disable');
45-
46-
// then
47-
expect(response.statusCode).to.equal(403);
48-
expect(membershipController.disable).to.have.not.been.called;
49-
});
50-
});
51-
527
describe('POST /api/memberships/{id}/disable', function () {
538
it('should return 204 if user is admin in organization', async function () {
549
// given

0 commit comments

Comments
 (0)