0001-api_entreprise-add-match_mandataire_social-endpoint-.patch
passerelle/apps/api_entreprise/models.py | ||
---|---|---|
33 | 33 |
from passerelle.utils.jsonresponse import APIError, exception_to_text |
34 | 34 |
from passerelle.views import WrongParameter |
35 | 35 | |
36 |
from .utils import simple_match |
|
37 | ||
36 | 38 |
DOCUMENT_SIGNATURE_MAX_AGE = timedelta(days=7) |
37 | 39 | |
38 | 40 | |
... | ... | |
170 | 172 |
'example_value': '2019', |
171 | 173 |
} |
172 | 174 | |
175 |
FIRST_NAME_PARAM = {'description': _('matched user\'s first name'), 'example_value': 'John'} |
|
176 | ||
177 |
LAST_NAME_PARAM = {'description': _('matched user\'s last name'), 'example_value': 'Doe'} |
|
178 | ||
179 |
BIRTHDATE_PARAM = {'description': _('matched user\'s YYYYDDMM birthdate'), 'example_value': '19700131'} |
|
180 | ||
181 |
METHOD_PARAM = {'description': _('method used for user identity matching'), 'example_value': 'simple'} |
|
182 | ||
173 | 183 |
@endpoint( |
174 | 184 |
perm='can_access', |
175 | 185 |
pattern=r'(?P<association_id>\w+)/$', |
... | ... | |
403 | 413 |
return self.get( |
404 | 414 |
'effectifs_mensuels_acoss_covid/%s/%s/etablissement/%s/' % (year, month, siret), **kwargs |
405 | 415 |
) |
416 | ||
417 |
@endpoint( |
|
418 |
perm='can_access', |
|
419 |
pattern=r'(?P<siren>\w+)/$', |
|
420 |
description=_( |
|
421 |
'Match firm\'s society representative against local FranceConnect identity information' |
|
422 |
), |
|
423 |
parameters={ |
|
424 |
'siren': SIREN_PARAM, |
|
425 |
'first_name': FIRST_NAME_PARAM, |
|
426 |
'last_name': LAST_NAME_PARAM, |
|
427 |
'birthdate': BIRTHDATE_PARAM, |
|
428 |
'method': METHOD_PARAM, |
|
429 |
'object': OBJECT_PARAM, |
|
430 |
'context': CONTEXT_PARAM, |
|
431 |
'recipient': RECIPIENT_PARAM, |
|
432 |
}, |
|
433 |
) |
|
434 |
def match_mandataire_social( |
|
435 |
self, request, siren, first_name, last_name, birthdate, method='simple', **kwargs |
|
436 |
): |
|
437 |
entreprise = self.get( |
|
438 |
'entreprises/%s/' % siren, |
|
439 |
**kwargs, |
|
440 |
) |
|
441 |
if method not in ('simple',): |
|
442 |
return {'err': 1, 'err_desc': 'method %s not implemented' % method} |
|
443 |
for mandataire in entreprise['data'].get('entreprise', {}).get('mandataires_sociaux', []): |
|
444 |
if method == 'simple' and simple_match(mandataire, first_name, last_name, birthdate): |
|
445 |
return {'err': 0, 'data': mandataire} |
|
446 |
return {'err': 0, 'data': {}} |
passerelle/apps/api_entreprise/utils.py | ||
---|---|---|
1 |
# passerelle - uniform access to multiple data sources and services |
|
2 |
# Copyright (C) 2022 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from unicodedata import category |
|
18 |
from unicodedata import normalize as unormalize |
|
19 | ||
20 | ||
21 |
def normalize(s): |
|
22 |
return ''.join(c for c in unormalize('NFKD', s).casefold() if category(c) not in ('Mn', 'Zs')) |
|
23 | ||
24 | ||
25 |
def simple_match(mandataire, first_name, last_name, birthdate): |
|
26 |
if any([attr not in mandataire for attr in ['prenom', 'nom', 'date_naissance']]): |
|
27 |
return False |
|
28 |
if normalize(mandataire['prenom'].split(',')[0]) != normalize(first_name): |
|
29 |
return False |
|
30 |
if normalize(mandataire['nom']) != normalize(last_name): |
|
31 |
return False |
|
32 |
if mandataire['date_naissance'].replace('-', '') != birthdate: |
|
33 |
return False |
|
34 |
return True |
tests/test_api_entreprise.py | ||
---|---|---|
386 | 386 |
assert data['etablissement_siege']['date_mise_a_jour'] == '2015-12-03' |
387 | 387 | |
388 | 388 | |
389 |
def test_match_mandataire_social(app, resource, mock_api_entreprise): |
|
390 |
params = { |
|
391 |
'first_name': 'françois', |
|
392 |
'last_name': 'hIsQuIn', |
|
393 |
'birthdate': '19650127', |
|
394 |
'method': 'simple', |
|
395 |
} |
|
396 |
params.update(REQUEST_PARAMS) |
|
397 | ||
398 |
url = '/api-entreprise/test/match_mandataire_social/443170139/' |
|
399 |
response = app.get(url, params=params) |
|
400 | ||
401 |
# successful matching |
|
402 |
data = response.json['data'] |
|
403 |
assert data['nom'] == 'HISQUIN' |
|
404 |
assert data['prenom'] == 'FRANCOIS' |
|
405 |
assert data['date_naissance'] == '1965-01-27' |
|
406 |
assert data['type'] == 'PP' |
|
407 | ||
408 |
params['method'] = 'unkwown' |
|
409 |
response = app.get(url, params=params) |
|
410 | ||
411 |
# unsupported method |
|
412 |
assert response.json['err'] |
|
413 |
assert 'data' not in response.json |
|
414 | ||
415 |
params['method'] = 'simple' |
|
416 |
params.pop('first_name') |
|
417 |
response = app.get(url, params=params, status=400) |
|
418 | ||
419 |
# missing parameter |
|
420 |
assert not response.json['data'] |
|
421 |
assert 'missing parameter' in response.json['err_desc'] |
|
422 | ||
423 |
params['first_name'] = 'JOHN' |
|
424 |
response = app.get(url, params=params) |
|
425 | ||
426 |
# matching failed |
|
427 |
assert not response.json['err'] |
|
428 |
assert not response.json['data'] |
|
429 | ||
430 | ||
389 | 431 |
@mock.patch('passerelle.utils.Request.get') |
390 | 432 |
def test_entreprises_endpoint_include_private(mocked_get, app, resource, mock_api_entreprise): |
391 | 433 |
mocked_get.return_value = FakedResponse(content='{}', status_code=200) |
392 |
- |