Projet

Général

Profil

0003-iparapheur-resolv-no-basic_authentication-on-w3.org-.patch

Nicolas Roche, 12 mars 2019 19:05

Télécharger (11,5 ko)

Voir les différences:

Subject: [PATCH 3/3] iparapheur: resolv no basic_authentication on w3.org
 (#31274)

 passerelle/contrib/iparapheur/models.py |  12 +--
 passerelle/utils/__init__.py            |  20 ++++-
 tests/test_iparapheur.py                | 115 +++++++++++++++++++++++-
 3 files changed, 140 insertions(+), 7 deletions(-)
passerelle/contrib/iparapheur/models.py
85 85
            raise APIError('ServiceError: %s' % exc)
86 86
        except TransportError as exc:
87 87
            raise APIError('Transport Error: %s' % exc)
88
        except TypeError as exc:
89
            raise APIError('Type Error: %s' % exc)
88 90
        return result
89 91

  
90 92
    @endpoint(perm='can_access')
......
142 144
                      }
143 145
        if email:
144 146
            parameters['EmailEmetteur'] = email
145
        rep = soap_client.service.CreerDossier(**parameters)
146
        if not rep or not rep.MessageRetour:
147
        resp = soap_client.service.CreerDossier(**parameters)
148
        if not resp or not resp.MessageRetour:
147 149
            raise FileError('unknown error, no response')
148
        if rep.MessageRetour.codeRetour == 'KO':
149
            raise FileError(rep.MessageRetour.message)
150
        return {'data': {'RecordId': rep.DossierID, 'message': rep.MessageRetour.message}}
150
        if resp.MessageRetour.codeRetour == 'KO':
151
            raise FileError(resp.MessageRetour.message)
152
        return {'data': {'RecordId': resp.DossierID, 'message': resp.MessageRetour.message}}
151 153

  
152 154
    @endpoint(perm='can_access', name='get-file', pattern='(?P<file_id>[\w-]+)')
153 155
    def get_file(self, request, file_id, appendix=None):
passerelle/utils/__init__.py
24 24
from requests import Session as RequestSession, Response as RequestResponse
25 25
from requests.structures import CaseInsensitiveDict
26 26
from urllib3.exceptions import InsecureRequestWarning
27
from django.utils.six.moves.urllib import parse as urlparse
27 28

  
28 29
from django.conf import settings
29 30
from django.core.cache import cache
......
288 289
        log_http_request(self.logger, request=request, response=response, exception=exception, error_log=error_log)
289 290

  
290 291

  
292
class SOAPTransport(Transport):
293
    """Wrapper around zeep.Transport
294

  
295
    disable basic_authentication hosts unrelated to wsdl's endpoints
296
    """
297
    def __init__(self, wsdl_url, **kwargs):
298
        self.wsdl_host = urlparse.urlparse(wsdl_url).netloc
299
        super(SOAPTransport, self).__init__(**kwargs)
300

  
301
    def _load_remote_data(self, url):
302
        if urlparse.urlparse(url).netloc != self.wsdl_host:
303
            response = self.session.get(url, timeout=self.load_timeout, auth=None, cert=None)
304
            response.raise_for_status()
305
            return response.content
306
        return super(SOAPTransport, self)._load_remote_data(url)
307

  
308

  
291 309
class SOAPClient(Client):
292 310
    """Wrapper around zeep.Client
293 311

  
294 312
    resource muste have a wsdl_url and a requests attribute
295 313
    """
296 314
    def __init__(self, resource, **kwargs):
297
        transport = Transport(session=resource.requests, cache=InMemoryCache())
298 315
        wsdl_url = kwargs.pop('wsdl_url', None) or resource.wsdl_url
316
        transport = SOAPTransport(wsdl_url, session=resource.requests, cache=InMemoryCache())
299 317
        super(SOAPClient, self).__init__(wsdl_url, transport=transport, **kwargs)
300 318

  
301 319

  
tests/test_iparapheur.py
54 54
    if url == 'https://secure-iparapheur.demonstrations.adullact.org:443/ws-iparapheur?wsdl':
55 55
        target_file = wsdl_file()
56 56

  
57
    # simulate second GET call (nested <xsd:import> into wsdl fil)
57
    # simulate second GET call (nested <xsd:import> into wsdl file)
58 58
    elif url == 'http://www.w3.org/2005/05/xmlmime':
59
        assert kwargs['auth'] is None  # no basic_authenticton on wsdl' imports
59 60
        target_file = xmlmime()
60 61

  
61 62
    else:
......
98 99
    typ = 'Courrier'
99 100
    subtyp = 'maire'
100 101
    visibility = 'SERVICE'
102
    email = 'alice.boniadetos@arf.pt'
101 103
    response = Response()
102 104
    response.status_code = 200
103 105

  
......
155 157
    assert 'zeep.exceptions.TransportError' in resp.json['err_class']
156 158
    assert 'Server returned HTTP status 200 (<nada>)' in resp.json['err_desc']
157 159

  
160
    # Unknown value for "visibility"
161
    data = {'type': typ, 'subtype': subtyp, 'visibility': 'UNKNOWN_VISIBILITY',
162
            'title': title, 'data': base64_data, 'content-type':'application/pdf'}
163
    url = reverse('generic-endpoint', kwargs={'connector': 'iparapheur',
164
                                    'endpoint': 'create-file', 'slug': conn.slug})
165
    url += '?apikey=%s' % API_KEY
166
    resp = app.post_json(url, params=data, status=500)
167
    assert resp.json['err'] == 1
168
    assert 'FileError' in resp.json['err_class']
169

  
170
    # OK, providing email
171
    data = {'type': typ, 'subtype': subtyp, 'visibility': visibility,
172
            'title': title, 'data': base64_data, 'content-type':'application/pdf',
173
            'email': email}
174
    url = reverse('generic-endpoint', kwargs={'connector': 'iparapheur',
175
                                    'endpoint': 'create-file', 'slug': conn.slug})
176
    url += '?apikey=%s' % API_KEY
177
    soap_response = """<nada>"""
178
    response._content = soap_response
179
    mocked_post.return_value = response
180
    resp = app.post_json(url, params=data, status=500)
181
    assert (BASE_URL,) == mocked_post.call_args[0]
182
    assert resp.json['err'] == 1
183
    assert 'zeep.exceptions.TransportError' in resp.json['err_class']
184
    assert 'Server returned HTTP status 200 (<nada>)' in resp.json['err_desc']
185

  
158 186
@mock.patch('passerelle.utils.Request.get', side_effect=iph_mocked_get)
159 187
@mock.patch('passerelle.utils.Request.post')
160 188
def test_files(mocked_post, mocked_get, app, conn):
......
175 203
        assert item['id']
176 204
        assert item['timestamp']
177 205

  
206
    # same call providing parameter
207
    soap_response = """<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><RechercherDossiersResponse xmlns="http://www.adullact.org/spring-ws/iparapheur/1.0" xmlns:xmime="http://www.w3.org/2005/05/xmlmime"><LogDossier><timestamp>2015-10-29T15:42:08.732+01:00</timestamp><nom>test</nom><status>Lu</status><annotation></annotation><accessible>KO</accessible></LogDossier></RechercherDossiersResponse></S:Body></S:Envelope>"""
208
    response._content = soap_response
209
    mocked_post.return_value = response
210
    url += '&status=Lu'
211
    resp = app.get(url)
212
    assert len(resp.json['data']) == 1
213
    for item in resp.json['data']:
214
        assert item['status']
215
        assert item['id']
216
        assert item['timestamp']
217

  
178 218
@mock.patch('passerelle.utils.Request.get', side_effect=iph_mocked_get)
179 219
@mock.patch('passerelle.utils.Request.post')
180 220
def test_get_file_status(mocked_post, mocked_get, app, conn):
......
428 468
    assert resp.json['err'] == 1
429 469
    assert resp.json['data'] is None
430 470
    assert 'mocked error' in resp.json['err_desc']
471

  
472
@mock.patch('passerelle.utils.Request.get')
473
@mock.patch('zeep.Transport._load_remote_data')
474
@mock.patch('passerelle.utils.Request.post')
475
def test_no_auth_on_wsdl_imports(mocked_post, mocked_load, mocked_get, app, conn):
476
    """
477
    While getting wsdl:
478
    - zeep.Transport._load_remote_data is call on basic_authentication (get wsdl file)
479
    - passerelle.utils.Request.get is call on no basic_authenticaion (get xmlmime import's file)
480

  
481
    Notice: this also test the 'echo' SOAP service
482
    """
483
    response_xmlmime, response_post = Response(), Response()
484
    response_xmlmime.status_code, response_post.status_code = 200, 200
485
    response_xmlmime._content=file(xmlmime()).read()
486
    response_post._content = """<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><echoResponse xmlns="http://www.adullact.org/spring-ws/iparapheur/1.0" xmlns:xmime="http://www.w3.org/2005/05/xmlmime">[publik_test] m'a dit: &quot;ping&quot;!</echoResponse></S:Body></S:Envelope>
487
"""
488
    mocked_load.return_value = file(wsdl_file()).read()
489
    mocked_get.return_value = response_xmlmime
490
    mocked_post.return_value = response_post
491
    url = reverse('generic-endpoint', kwargs={'connector': 'iparapheur',
492
                    'endpoint': 'ping', 'slug': conn.slug})
493

  
494
    resp = app.get(url, status=403)
495
    url += '?apikey=%s' % API_KEY
496
    resp = app.get(url)
497
    assert resp.json['err'] == 0
498
    assert resp.json['data'] == "[publik_test] m'a dit: \"ping\"!"
499

  
500
@mock.patch('passerelle.utils.Request.get', side_effect=iph_mocked_get)
501
@mock.patch('passerelle.utils.Request.post')
502
def test_types(mocked_post, mocked_get, app, conn):
503
    """test GetListeTypesRequest SOAP service"""
504
    response = Response()
505
    response.status_code = 200
506

  
507
    # empty list of types as response
508
    soap_response = """<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><GetListeTypesResponse xmlns="http://www.adullact.org/spring-ws/iparapheur/1.0" xmlns:xmime="http://www.w3.org/2005/05/xmlmime"/></S:Body></S:Envelope>"""
509

  
510
    response._content = soap_response
511
    mocked_post.return_value = response
512
    url = reverse('generic-endpoint', kwargs={'slug': conn.slug, 'connector': 'iparapheur',
513
                          'endpoint': 'types'})
514
    url += '?apikey=%s' % API_KEY
515
    resp = app.get(url)
516
    assert resp.json['err'] == 0
517
    assert resp.json['data'] == []
518

  
519
@mock.patch('passerelle.utils.Request.get', side_effect=iph_mocked_get)
520
@mock.patch('passerelle.utils.Request.post')
521
def test_subtypes(mocked_post, mocked_get, app, conn):
522
    """test GetListeSousTypes SOAP service"""
523
    response = Response()
524
    response.status_code = 200
525

  
526
    # error: no parameter provided
527
    url = reverse('generic-endpoint', kwargs={'slug': conn.slug, 'connector': 'iparapheur',
528
                          'endpoint': 'subtypes'})
529
    resp = app.get(url, status=403)
530
    url += '?apikey=%s' % API_KEY
531
    resp = app.get(url)
532
    assert resp.json['err'] == 1
533
    assert resp.json['err_desc'] == "Type Error: TypeTechnique() takes exactly 1 argument (0 given). Simple types expect only a single value argument"
534

  
535
    # providing a type as parameter
536
    soap_response = """<?xml version='1.0' encoding='UTF-8'?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><GetListeTypesResponse xmlns="http://www.adullact.org/spring-ws/iparapheur/1.0" xmlns:xmime="http://www.w3.org/2005/05/xmlmime"/></S:Body></S:Envelope>"""
537

  
538
    response._content = soap_response  # empty list of subtypes as response
539
    mocked_post.return_value = response
540
    url += '&type=my_unknown_type'
541
    resp = app.get(url)
542
    assert resp.json['err'] == 0
543
    assert resp.json['data'] == []
431
-