Projet

Général

Profil

0001-api-add-basic-auth-support-to-users-xxx-and-users-xx.patch

Lauréline Guérin, 23 septembre 2021 10:48

Télécharger (12,2 ko)

Voir les différences:

Subject: [PATCH] api: add basic auth support to users/xxx and users/xxx/forms
 (#56908)

 tests/api/test_user.py | 183 ++++++++++++++++++++++++++++-------------
 wcs/api.py             |  16 ++--
 2 files changed, 138 insertions(+), 61 deletions(-)
tests/api/test_user.py
1 1
import datetime
2 2
import os
3
from functools import partial
3 4

  
4 5
import pytest
5 6
from quixote import get_publisher
......
77 78
    return user
78 79

  
79 80

  
81
def _get_url(url, app, auth, access, user, **kwargs):
82
    if auth == 'http-basic':
83
        app.set_authorization(('Basic', ('test', '12345')))
84
        return app.get(url, **kwargs)
85
    else:
86
        return app.get(
87
            sign_uri(url, user=user, orig=access.access_identifier, key=access.access_key), **kwargs
88
        )
89

  
90

  
91
@pytest.fixture
92
def access(pub):
93
    ApiAccess.wipe()
94
    access = ApiAccess()
95
    access.name = 'test'
96
    access.access_identifier = 'test'
97
    access.access_key = '12345'
98
    access.store()
99
    return access
100

  
101

  
80 102
def test_roles(pub, local_user):
81 103
    pub.role_class.wipe()
82 104
    role = pub.role_class(name='Hello World')
......
102 124
    assert resp.json['data'][0]['details'] == 'kouign amann'
103 125

  
104 126

  
127
def test_user_api_with_restricted_access(pub, access):
128
    role = pub.role_class(name='test')
129
    role.store()
130

  
131
    access.roles = [role]
132
    access.store()
133

  
134
    resp = get_app(pub).get(sign_uri('/api/user/', orig='test', key='12345'), status=403)
135
    assert resp.json['err'] == 1
136
    assert resp.json['err_desc'] == 'restricted API access'
137

  
138

  
139
def test_users_api_with_restricted_access(pub, local_user, access):
140
    role = pub.role_class(name='test')
141
    role.store()
142

  
143
    access.roles = [role]
144
    access.store()
145

  
146
    resp = get_app(pub).get(sign_uri('/api/users/', orig='test', key='12345'), status=403)
147
    assert resp.json['err'] == 1
148
    assert resp.json['err_desc'] == 'restricted API access'
149

  
150

  
105 151
def test_users(pub, local_user):
106 152
    resp = get_app(pub).get('/api/users/', status=403)
107 153

  
......
199 245
    )
200 246

  
201 247

  
202
def test_user_by_nameid(pub, local_user):
203
    resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user), status=404)
248
@pytest.mark.parametrize('auth', ['signature', 'http-basic'])
249
def test_user_by_nameid(pub, local_user, access, auth):
250
    app = get_app(pub)
251
    get_url = partial(_get_url, app=app, auth=auth, access=access, user=local_user)
252

  
253
    resp = get_url('/api/users/xyz/', status=404)
204 254
    local_user.name_identifiers = ['xyz']
205 255
    local_user.store()
206
    resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user))
256
    resp = get_url('/api/users/xyz/')
207 257
    assert str(resp.json['id']) == str(local_user.id)
208 258

  
209 259

  
210
def test_user_roles(pub, local_user):
211
    local_user.name_identifiers = ['xyz']
212
    local_user.store()
260
def test_user_by_nameid_api_access_restrict_to_anonymised_data(pub, local_user, access):
261
    app = get_app(pub)
262
    get_url = partial(_get_url, app=app, auth='http-basic', access=access, user=local_user)
263

  
264
    get_url('/api/users/%s/' % local_user.id)
265

  
266
    access.restrict_to_anonymised_data = True
267
    access.store()
268
    resp = get_url('/api/users/%s/' % local_user.id, status=403)
269
    assert resp.json['err'] == 1
270
    assert resp.json['err_desc'] == 'restricted API access'
271

  
272

  
273
@pytest.mark.parametrize('auth', ['signature', 'http-basic'])
274
def test_user_roles(pub, local_user, access, auth):
213 275
    role = pub.role_class(name='Foo bar')
214 276
    role.store()
277
    local_user.name_identifiers = ['xyz']
215 278
    local_user.roles = [role.id]
216 279
    local_user.store()
217
    resp = get_app(pub).get(sign_uri('/api/users/xyz/', user=local_user))
280

  
281
    app = get_app(pub)
282
    get_url = partial(_get_url, app=app, auth=auth, access=access, user=local_user)
283

  
284
    resp = get_url('/api/users/xyz/')
218 285
    assert len(resp.json['user_roles']) == 1
219 286
    assert resp.json['user_roles'][0]['name'] == 'Foo bar'
220 287

  
221 288

  
222
def test_user_forms(pub, local_user):
289
def test_user_forms(pub, local_user, access):
223 290
    Workflow.wipe()
224 291
    workflow = Workflow.get_default_workflow()
225 292
    workflow.id = '2'
......
339 406
    role = pub.role_class(name='test')
340 407
    role.store()
341 408

  
342
    access = ApiAccess()
343
    access.name = 'test'
344
    access.access_identifier = 'test'
345
    access.access_key = '12345'
346 409
    access.roles = [role]
347 410
    access.store()
348 411

  
......
351 414
    assert resp.json['err_desc'] == 'restricted API access'
352 415

  
353 416

  
354
def test_user_api_with_restricted_access(pub):
355
    role = pub.role_class(name='test')
356
    role.store()
357

  
358
    access = ApiAccess()
359
    access.name = 'test'
360
    access.access_identifier = 'test'
361
    access.access_key = '12345'
362
    access.roles = [role]
363
    access.store()
364

  
365
    resp = get_app(pub).get(sign_uri('/api/user/', orig='test', key='12345'), status=403)
366
    assert resp.json['err'] == 1
367
    assert resp.json['err_desc'] == 'restricted API access'
368

  
369

  
370
def test_users_api_with_restricted_access(pub, local_user):
371
    role = pub.role_class(name='test')
372
    role.store()
373

  
374
    access = ApiAccess()
375
    access.name = 'test'
376
    access.access_identifier = 'test'
377
    access.access_key = '12345'
378
    access.roles = [role]
379
    access.store()
380

  
381
    resp = get_app(pub).get(sign_uri('/api/users/', orig='test', key='12345'), status=403)
382
    assert resp.json['err'] == 1
383
    assert resp.json['err_desc'] == 'restricted API access'
384

  
385
    resp = get_app(pub).get(sign_uri('/api/users/%s/' % local_user.id, orig='test', key='12345'), status=403)
386
    assert resp.json['err'] == 1
387
    assert resp.json['err_desc'] == 'restricted API access'
388

  
389

  
390 417
def test_user_forms_limit_offset(pub, local_user):
391 418
    if not pub.is_using_postgresql():
392 419
        pytest.skip('this requires SQL')
......
496 523
    assert len(resp.json['data']) == 5
497 524

  
498 525

  
499
def test_user_forms_from_agent(pub, local_user):
526
@pytest.mark.parametrize('auth', ['signature', 'http-basic'])
527
def test_user_forms_from_agent(pub, local_user, access, auth):
500 528
    pub.role_class.wipe()
501 529
    role = pub.role_class(name='Foo bar')
502 530
    role.store()
......
525 553
    formdata.jump_status('new')
526 554
    formdata.store()
527 555

  
528
    resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user))
556
    if auth == 'http-basic':
557
        access.roles = [role]
558
        access.store()
559

  
560
    app = get_app(pub)
561
    get_url = partial(_get_url, app=app, auth=auth, access=access, user=agent_user)
562

  
563
    resp = get_url('/api/users/%s/forms' % local_user.id)
529 564
    assert resp.json['err'] == 0
530 565
    assert len(resp.json['data']) == 1
531 566
    assert resp.json['data'][0]['form_name'] == 'test'
......
536 571
    formdef.skip_from_360_view = True
537 572
    formdef.store()
538 573

  
539
    resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user))
574
    resp = get_url('/api/users/%s/forms' % local_user.id)
540 575
    assert len(resp.json['data']) == 0
541 576

  
542 577
    formdef.workflow_roles = {'_receiver': str(role.id)}
543 578
    formdef.store()
544 579
    formdef.data_class().rebuild_security()
545
    resp = get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user))
580
    resp = get_url('/api/users/%s/forms' % local_user.id)
546 581
    assert len(resp.json['data']) == 1
547 582

  
548 583
    agent_user.roles = []
549 584
    agent_user.store()
550
    get_app(pub).get(sign_uri('/api/users/%s/forms' % local_user.id, user=agent_user), status=403)
585
    if auth == 'http-basic':
586
        access.roles = []
587
        access.store()
588
        resp = get_url('/api/users/%s/forms' % local_user.id)
589
        assert len(resp.json['data']) == 0
590
    else:
591
        get_url('/api/users/%s/forms' % local_user.id, status=403)
592

  
593

  
594
def test_user_forms_api_access_restrict_to_anonymised_data(pub, local_user, access):
595
    FormDef.wipe()
596
    formdef = FormDef()
597
    formdef.name = 'test'
598
    formdef.fields = []
599
    formdef.store()
600

  
601
    app = get_app(pub)
602
    get_url = partial(_get_url, app=app, auth='http-basic', access=access, user=local_user)
603

  
604
    get_url('/api/users/%s/forms' % local_user.id)
605

  
606
    access.restrict_to_anonymised_data = True
607
    access.store()
608
    resp = get_url('/api/users/%s/forms' % local_user.id, status=403)
609
    assert resp.json['err'] == 1
610
    assert resp.json['err_desc'] == 'restricted API access'
551 611

  
552 612

  
553
def test_user_forms_include_accessible(pub, local_user):
613
def test_user_forms_include_accessible(pub, local_user, access):
554 614
    if not pub.is_using_postgresql():
555 615
        pytest.skip('this requires SQL')
556 616
        return
......
611 671
    formdata4.jump_status('new')
612 672
    formdata4.store()
613 673

  
674
    app = get_app(pub)
675

  
614 676
    def get_ids(url):
615
        resp = get_app(pub).get(url)
677
        resp = app.get(url)
616 678
        return {int(x['form_number_raw']) for x in resp.json['data']}
617 679

  
618 680
    resp = get_ids(sign_uri('/api/user/forms', user=local_user))
......
628 690
    resp = get_ids(sign_uri('/api/users/%s/forms?include-accessible=on' % local_user.id, user=agent_user))
629 691
    assert resp == {formdata1.id, formdata3.id}
630 692

  
693
    # an api access gets the same results
694
    access.roles = [role]
695
    access.store()
696
    app.set_authorization(('Basic', ('test', '12345')))
697

  
698
    resp = get_ids('/api/users/%s/forms' % local_user.id)
699
    assert resp == {formdata1.id}
700

  
701
    resp = get_ids('/api/users/%s/forms?include-accessible=on' % local_user.id)
702
    assert resp == {formdata1.id, formdata3.id}
703

  
631 704

  
632 705
def test_user_drafts(pub, local_user):
633 706
    FormDef.wipe()
wcs/api.py
968 968
        if user.is_api_user:
969 969
            raise AccessForbiddenError('restricted API access')
970 970

  
971
        query_user = get_user_from_api_query_string() or get_request().user
972
        if query_user and query_user.is_api_user and query_user.api_access.restrict_to_anonymised_data:
973
            raise AccessForbiddenError('restricted API access')
974

  
971 975
        forms = self.get_user_forms(user)
972 976

  
973 977
        if self.user:
974 978
            # call to /api/users/<id>/forms, this returns the forms of the
975 979
            # given user filtered according to the permissions of the caller
976 980
            # (from query string or session).
977
            query_user = get_user_from_api_query_string() or get_request().user
978 981
            if query_user and query_user.id != self.user.id:
979
                if not query_user.can_go_in_backoffice():
982
                if not query_user.is_api_user and not query_user.can_go_in_backoffice():
980 983
                    raise AccessForbiddenError('user not allowed to query data from others')
981 984
                # mark forms that are readable by querying user
982 985
                user_roles = set(query_user.get_roles())
......
1081 1084
        return json.dumps({'data': data, 'err': 0}, cls=misc.JSONEncoder)
1082 1085

  
1083 1086
    def _q_lookup(self, component):
1084
        if not (is_url_signed() or (get_request().user and get_request().user.can_go_in_admin())):
1085
            raise AccessForbiddenError('unsigned request or user has no access to backoffice')
1086

  
1087 1087
        api_user = get_user_from_api_query_string()
1088 1088
        if api_user and api_user.is_api_user:
1089
            raise AccessForbiddenError('restricted API access')
1089
            # API users are ok except if they are restricted to anonymised data
1090
            if api_user.api_access.restrict_to_anonymised_data:
1091
                raise AccessForbiddenError('restricted API access')
1092
        elif not (is_url_signed() or (get_request().user and get_request().user.can_go_in_admin())):
1093
            raise AccessForbiddenError('unsigned request or user has no access to backoffice')
1090 1094

  
1091 1095
        user_class = get_publisher().user_class
1092 1096
        try:
1093
-