Skip to content

Commit e6af37c

Browse files
[TECH] Dans Pix API supprimer les notions erronées ou obsolètes de scope et de target utilisées pour la vérification du droit d’accès d’un utilisateur à une application (PIX-15945)
#11513
2 parents 91cf2fb + 1b2f808 commit e6af37c

23 files changed

+245
-316
lines changed

api/src/authorization/domain/constants.js

+1-8
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,11 @@ const PIX_ADMIN = {
66
SUPER_ADMIN: 'SUPER_ADMIN',
77
SUPPORT: 'SUPPORT',
88
},
9-
SCOPE: 'pix-admin',
10-
TARGET: 'admin',
119
};
1210

1311
const PIX_ORGA = {
1412
NOT_LINKED_ORGANIZATION_MSG:
1513
"L'accès à Pix Orga est limité aux membres invités. Chaque espace est géré par un administrateur Pix Orga propre à l'organisation qui l'utilise. Contactez-le pour qu'il vous y invite.",
16-
SCOPE: 'pix-orga',
1714
};
1815

19-
const PIX_CERTIF = {
20-
SCOPE: 'pix-certif',
21-
};
22-
23-
export { PIX_ADMIN, PIX_CERTIF, PIX_ORGA };
16+
export { PIX_ADMIN, PIX_ORGA };

api/src/identity-access-management/application/oidc-provider/oidc-provider.admin.controller.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { oidcAuthenticationServiceRegistry } from '../../../../lib/domain/usecases/index.js';
2-
import { PIX_ADMIN } from '../../../authorization/domain/constants.js';
32
import { DomainTransaction } from '../../../shared/domain/DomainTransaction.js';
43
import { usecases } from '../../domain/usecases/index.js';
54
import * as oidcProviderSerializer from '../../infrastructure/serializers/jsonapi/oidc-identity-providers.serializer.js';
@@ -52,7 +51,7 @@ async function reconcileUserForAdmin(
5251

5352
const oidcAuthenticationService = dependencies.oidcAuthenticationServiceRegistry.getOidcProviderServiceByCode({
5453
identityProviderCode: identityProvider,
55-
target: PIX_ADMIN.TARGET,
54+
requestedApplication,
5655
});
5756

5857
const accessToken = await usecases.reconcileOidcUserForAdmin({

api/src/identity-access-management/application/oidc-provider/oidc-provider.controller.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { getForwardedOrigin, RequestedApplication } from '../../infrastructure/u
1212
* @return {Promise<*>}
1313
*/
1414
async function authenticateOidcUser(request, h) {
15-
const { code, state, iss, identityProvider: identityProviderCode, target } = request.deserializedPayload;
15+
const { code, state, iss, identityProvider: identityProviderCode } = request.deserializedPayload;
1616
const origin = getForwardedOrigin(request.headers);
1717
const requestedApplication = RequestedApplication.fromOrigin(origin);
1818

@@ -25,7 +25,6 @@ async function authenticateOidcUser(request, h) {
2525
}
2626

2727
const result = await usecases.authenticateOidcUser({
28-
target,
2928
code,
3029
state,
3130
iss,
@@ -104,9 +103,11 @@ async function findUserForReconciliation(request, h, dependencies = { oidcSerial
104103
* @return {Promise<Object>}
105104
*/
106105
async function getAuthorizationUrl(request, h) {
107-
const { identity_provider: identityProvider, target } = request.query;
106+
const { identity_provider: identityProvider } = request.query;
107+
const origin = getForwardedOrigin(request.headers);
108+
const requestedApplication = RequestedApplication.fromOrigin(origin);
108109

109-
const { nonce, state, ...payload } = await usecases.getAuthorizationUrl({ target, identityProvider });
110+
const { nonce, state, ...payload } = await usecases.getAuthorizationUrl({ identityProvider, requestedApplication });
110111

111112
request.yar.set('state', state);
112113
request.yar.set('nonce', nonce);
@@ -122,8 +123,11 @@ async function getAuthorizationUrl(request, h) {
122123
* @return {Promise<*>}
123124
*/
124125
async function getIdentityProviders(request, h) {
125-
const target = request.query.target;
126-
const identityProviders = await usecases.getReadyIdentityProviders({ target });
126+
const origin = getForwardedOrigin(request.headers);
127+
const requestedApplication = RequestedApplication.fromOrigin(origin);
128+
129+
const identityProviders = await usecases.getReadyIdentityProviders({ requestedApplication });
130+
127131
return h.response(oidcProviderSerializer.serialize(identityProviders)).code(200);
128132
}
129133

api/src/identity-access-management/application/oidc-provider/oidc-provider.route.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const oidcProviderRoutes = [
1010
options: {
1111
validate: {
1212
query: Joi.object({
13-
target: Joi.string().optional().default('app'),
13+
target: Joi.string().optional().default('app'), // Now useless, will soon be removed
1414
}),
1515
},
1616
auth: false,
@@ -49,7 +49,7 @@ export const oidcProviderRoutes = [
4949
validate: {
5050
query: Joi.object({
5151
identity_provider: Joi.string().required(),
52-
target: Joi.string().valid('app', 'admin').optional(),
52+
target: Joi.string().valid('app', 'admin').optional(), // Now useless, will soon be removed
5353
}),
5454
},
5555
handler: (request, h) => oidcProviderController.getAuthorizationUrl(request, h),
@@ -73,7 +73,7 @@ export const oidcProviderRoutes = [
7373
code: Joi.string().required(),
7474
state: Joi.string().required(),
7575
iss: Joi.string().optional(),
76-
target: Joi.string().valid('app', 'admin').optional(),
76+
target: Joi.string().valid('app', 'admin').optional(), // Now useless, will soon be removed
7777
},
7878
},
7979
}),

api/src/identity-access-management/application/token/token.controller.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { tokenService } from '../../../shared/domain/services/token-service.js';
22
import { usecases } from '../../domain/usecases/index.js';
3-
import { getForwardedOrigin } from '../../infrastructure/utils/network.js';
3+
import { getForwardedOrigin, RequestedApplication } from '../../infrastructure/utils/network.js';
44

55
const authenticateAnonymousUser = async function (request, h) {
66
const { campaign_code: campaignCode, lang } = request.payload;
@@ -28,21 +28,22 @@ const createToken = async function (request, h, dependencies = { tokenService })
2828
let expirationDelaySeconds;
2929

3030
const origin = getForwardedOrigin(request.headers);
31+
const requestedApplication = RequestedApplication.fromOrigin(origin);
3132

3233
const grantType = request.payload.grant_type;
3334

3435
if (grantType === 'password') {
35-
const { username, password, scope } = request.payload;
36+
const { username, password } = request.payload;
3637
const localeFromCookie = request.state?.locale;
3738
const source = 'pix';
3839

3940
const tokensInfo = await usecases.authenticateUser({
4041
username,
4142
password,
42-
scope,
4343
source,
4444
localeFromCookie,
4545
audience: origin,
46+
requestedApplication,
4647
});
4748

4849
accessToken = tokensInfo.accessToken;

api/src/identity-access-management/application/token/token.route.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const tokenRoutes = [
2121
grant_type: Joi.string().valid('password').required(),
2222
username: Joi.string().required(),
2323
password: Joi.string().required(),
24-
scope: Joi.string(),
24+
scope: Joi.string().optional(), // Now useless, will soon be removed
2525
}),
2626
Joi.object()
2727
.required()

api/src/identity-access-management/domain/services/oidc-authentication-service-registry.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ export class OidcAuthenticationServiceRegistry {
4444
return this.#readyOidcProviderServicesForPixAdmin;
4545
}
4646

47-
getOidcProviderServiceByCode({ identityProviderCode, target = 'app' }) {
48-
const services = target === 'admin' ? this.#readyOidcProviderServicesForPixAdmin : this.#readyOidcProviderServices;
47+
getOidcProviderServiceByCode({ identityProviderCode, requestedApplication }) {
48+
const services = requestedApplication?.isPixAdmin
49+
? this.#readyOidcProviderServicesForPixAdmin
50+
: this.#readyOidcProviderServices;
4951
const oidcProviderService = services.find((service) => identityProviderCode === service.code);
5052

5153
if (!oidcProviderService) {

api/src/identity-access-management/domain/usecases/authenticate-oidc-user.usecase.js

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { PIX_ADMIN } from '../../../authorization/domain/constants.js';
21
import { ForbiddenAccess } from '../../../shared/domain/errors.js';
32

43
/**
@@ -11,40 +10,39 @@ import { ForbiddenAccess } from '../../../shared/domain/errors.js';
1110
* @param {string} params.sessionState
1211
* @param {string} params.state
1312
* @param {string} params.audience
13+
* @param {RequestedApplication} params.requestedApplication
1414
* @param {AuthenticationSessionService} params.authenticationSessionService
1515
* @param {OidcAuthenticationServiceRegistry} params.oidcAuthenticationServiceRegistry
1616
* @param {AdminMemberRepository} params.adminMemberRepository
1717
* @param {AuthenticationMethodRepository} params.authenticationMethodRepository
1818
* @param {UserLoginRepository} params.userLoginRepository
1919
* @param {UserRepository} params.userRepository
2020
* @param {LastUserApplicationConnectionsRepository} params.LastUserApplicationConnectionsRepository,
21-
* @param {RequestedApplication} params.RequestedApplication,
2221
* @return {Promise<{isAuthenticationComplete: boolean, givenName: string, familyName: string, authenticationKey: string, email: string}|{isAuthenticationComplete: boolean, pixAccessToken: string, logoutUrlUUID: string}>}
2322
*/
2423
async function authenticateOidcUser({
25-
target,
2624
code,
2725
state,
2826
iss,
2927
identityProviderCode,
3028
nonce,
3129
sessionState,
3230
audience,
31+
requestedApplication,
3332
authenticationSessionService,
3433
oidcAuthenticationServiceRegistry,
3534
adminMemberRepository,
3635
authenticationMethodRepository,
3736
userLoginRepository,
3837
userRepository,
3938
lastUserApplicationConnectionsRepository,
40-
requestedApplication,
4139
}) {
4240
await oidcAuthenticationServiceRegistry.loadOidcProviderServices();
4341
await oidcAuthenticationServiceRegistry.configureReadyOidcProviderServiceByCode(identityProviderCode);
4442

4543
const oidcAuthenticationService = oidcAuthenticationServiceRegistry.getOidcProviderServiceByCode({
4644
identityProviderCode,
47-
target,
45+
requestedApplication,
4846
});
4947

5048
const sessionContent = await oidcAuthenticationService.exchangeCodeForTokens({
@@ -69,7 +67,7 @@ async function authenticateOidcUser({
6967
return { authenticationKey, givenName, familyName, email, isAuthenticationComplete: false };
7068
}
7169

72-
await _assertUserWithPixAdminAccess({ target, userId: user.id, adminMemberRepository });
70+
await _assertUserHasAccessToApplication({ requestedApplication, user, adminMemberRepository });
7371

7472
await _updateAuthenticationMethodWithComplement({
7573
userInfo,
@@ -120,9 +118,9 @@ async function _updateAuthenticationMethodWithComplement({
120118
});
121119
}
122120

123-
async function _assertUserWithPixAdminAccess({ target, userId, adminMemberRepository }) {
124-
if (target === PIX_ADMIN.TARGET) {
125-
const adminMember = await adminMemberRepository.get({ userId });
121+
async function _assertUserHasAccessToApplication({ requestedApplication, user, adminMemberRepository }) {
122+
if (requestedApplication.isPixAdmin) {
123+
const adminMember = await adminMemberRepository.get({ userId: user.id });
126124
if (!adminMember?.hasAccessToAdminScope) {
127125
throw new ForbiddenAccess(
128126
'User does not have the rights to access the application',

api/src/identity-access-management/domain/usecases/authenticate-user.js

+22-23
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { RefreshToken } from '../models/RefreshToken.js';
77

88
const authenticateUser = async function ({
99
password,
10-
scope,
1110
source,
1211
username,
1312
localeFromCookie,
@@ -21,52 +20,52 @@ const authenticateUser = async function ({
2120
emailRepository,
2221
emailValidationDemandRepository,
2322
audience,
23+
requestedApplication,
2424
}) {
2525
try {
26-
const foundUser = await pixAuthenticationService.getUserByUsernameAndPassword({
26+
const user = await pixAuthenticationService.getUserByUsernameAndPassword({
2727
username,
2828
password,
2929
userRepository,
3030
});
3131

32-
if (foundUser.shouldChangePassword) {
33-
const passwordResetToken = tokenService.createPasswordResetToken(foundUser.id);
32+
if (user.shouldChangePassword) {
33+
const passwordResetToken = tokenService.createPasswordResetToken(user.id);
3434
throw new UserShouldChangePasswordError(undefined, passwordResetToken);
3535
}
3636

37-
await _checkUserAccessScope(scope, foundUser, adminMemberRepository);
37+
await _assertUserHasAccessToApplication({ requestedApplication, user, adminMemberRepository });
3838

39-
const refreshToken = RefreshToken.generate({ userId: foundUser.id, source, audience });
39+
const refreshToken = RefreshToken.generate({ userId: user.id, source, audience });
4040
await refreshTokenRepository.save({ refreshToken });
4141

4242
const { accessToken, expirationDelaySeconds } = await tokenService.createAccessTokenFromUser({
43-
userId: foundUser.id,
43+
userId: user.id,
4444
source,
4545
audience,
4646
});
4747

48-
foundUser.setLocaleIfNotAlreadySet(localeFromCookie);
49-
if (foundUser.hasBeenModified) {
50-
await userRepository.update({ id: foundUser.id, locale: foundUser.locale });
48+
user.setLocaleIfNotAlreadySet(localeFromCookie);
49+
if (user.hasBeenModified) {
50+
await userRepository.update({ id: user.id, locale: user.locale });
5151
}
52-
const userLogin = await userLoginRepository.findByUserId(foundUser.id);
53-
if (foundUser.email && userLogin?.shouldSendConnectionWarning()) {
54-
const validationToken = !foundUser.emailConfirmedAt
55-
? await emailValidationDemandRepository.save(foundUser.id)
56-
: null;
52+
53+
const userLogin = await userLoginRepository.findByUserId(user.id);
54+
if (user.email && userLogin?.shouldSendConnectionWarning()) {
55+
const validationToken = !user.emailConfirmedAt ? await emailValidationDemandRepository.save(user.id) : null;
5756
await emailRepository.sendEmailAsync(
5857
createWarningConnectionEmail({
59-
locale: foundUser.locale,
60-
email: foundUser.email,
61-
firstName: foundUser.firstName,
58+
locale: user.locale,
59+
email: user.email,
60+
firstName: user.firstName,
6261
validationToken,
6362
}),
6463
);
6564
}
66-
await userLoginRepository.updateLastLoggedAt({ userId: foundUser.id });
65+
await userLoginRepository.updateLastLoggedAt({ userId: user.id });
6766

6867
await authenticationMethodRepository.updateLastLoggedAtByIdentityProvider({
69-
userId: foundUser.id,
68+
userId: user.id,
7069
identityProvider: NON_OIDC_IDENTITY_PROVIDERS.PIX.code,
7170
});
7271

@@ -80,12 +79,12 @@ const authenticateUser = async function ({
8079
}
8180
};
8281

83-
async function _checkUserAccessScope(scope, user, adminMemberRepository) {
84-
if (scope === PIX_ORGA.SCOPE && !user.isLinkedToOrganizations()) {
82+
async function _assertUserHasAccessToApplication({ requestedApplication, user, adminMemberRepository }) {
83+
if (requestedApplication.isPixOrga && !user.isLinkedToOrganizations()) {
8584
throw new ForbiddenAccess(PIX_ORGA.NOT_LINKED_ORGANIZATION_MSG);
8685
}
8786

88-
if (scope === PIX_ADMIN.SCOPE) {
87+
if (requestedApplication.isPixAdmin) {
8988
const adminMember = await adminMemberRepository.get({ userId: user.id });
9089
if (!adminMember?.hasAccessToAdminScope) {
9190
throw new ForbiddenAccess(PIX_ADMIN.NOT_ALLOWED_MSG);

api/src/identity-access-management/domain/usecases/get-authorization-url.usecase.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
/**
22
* @typedef {function} getAuthorizationUrl
33
* @param {Object} params
4-
* @param {string} params.audience
54
* @param {string} params.identityProvider
5+
* @param {RequestedApplication} params.requestedApplication
66
* @param {OidcAuthenticationServiceRegistry} params.oidcAuthenticationServiceRegistry
77
* @return {Promise<string>}
88
*/
9-
async function getAuthorizationUrl({ target, identityProvider, oidcAuthenticationServiceRegistry }) {
9+
async function getAuthorizationUrl({ identityProvider, requestedApplication, oidcAuthenticationServiceRegistry }) {
1010
await oidcAuthenticationServiceRegistry.loadOidcProviderServices();
1111
await oidcAuthenticationServiceRegistry.configureReadyOidcProviderServiceByCode(identityProvider);
1212

1313
const oidcAuthenticationService = oidcAuthenticationServiceRegistry.getOidcProviderServiceByCode({
1414
identityProviderCode: identityProvider,
15-
target,
15+
requestedApplication,
1616
});
1717

1818
return oidcAuthenticationService.getAuthorizationUrl();

api/src/identity-access-management/domain/usecases/get-ready-identity-providers.usecase.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
/**
22
* @typedef {function} getReadyIdentityProviders
33
* @param {Object} params
4-
* @param {string} [params.audience=app]
4+
* @param {RequestedApplication} params.requestedApplication
55
* @param {OidcAuthenticationServiceRegistry} params.oidcAuthenticationServiceRegistry
66
* @return {Promise<OidcAuthenticationService[]|null>}
77
*/
8-
const getReadyIdentityProviders = async function ({ target = 'app', oidcAuthenticationServiceRegistry }) {
8+
const getReadyIdentityProviders = async function ({ requestedApplication, oidcAuthenticationServiceRegistry }) {
99
await oidcAuthenticationServiceRegistry.loadOidcProviderServices();
1010

11-
if (target === 'admin') {
11+
if (requestedApplication?.isPixAdmin) {
1212
return oidcAuthenticationServiceRegistry.getReadyOidcProviderServicesForPixAdmin();
1313
}
1414

api/src/identity-access-management/domain/usecases/reconcile-oidc-user.usecase.js

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { AuthenticationMethod } from '../models/AuthenticationMethod.js';
77
* @param {string} params.authenticationKey
88
* @param {string} params.identityProvider
99
* @param {string} params.audience
10+
* @param {RequestedApplication} params.requestedApplication
1011
* @param {AuthenticationSessionService} params.authenticationSessionService
1112
* @param {AuthenticationMethodRepository} params.authenticationMethodRepository
1213
* @param {OidcAuthenticationServiceRegistry} params.oidcAuthenticationServiceRegistry
@@ -31,6 +32,7 @@ export const reconcileOidcUser = async function ({
3132

3233
const oidcAuthenticationService = oidcAuthenticationServiceRegistry.getOidcProviderServiceByCode({
3334
identityProviderCode: identityProvider,
35+
requestedApplication,
3436
});
3537

3638
const sessionContentAndUserInfo = await authenticationSessionService.getByKey(authenticationKey);

0 commit comments

Comments
 (0)