Projet

Général

Profil

0001-api-allow-mixing-anonymous-restriction-and-basic-aut.patch

Frédéric Péters, 10 mai 2021 15:28

Télécharger (7,83 ko)

Voir les différences:

Subject: [PATCH] api: allow mixing anonymous restriction and basic
 authentication (#53883)

 tests/api/test_formdata.py | 59 ++++++++++++++++++++++++++------------
 wcs/api.py                 | 24 +++++++++-------
 wcs/api_access.py          |  5 +++-
 wcs/forms/common.py        |  5 ++--
 wcs/qommon/http_request.py |  5 ++++
 5 files changed, 64 insertions(+), 34 deletions(-)
tests/api/test_formdata.py
58 58
'''
59 59
        )
60 60

  
61
    pub.user_class.wipe()
62

  
61 63
    return pub
62 64

  
63 65

  
......
67 69

  
68 70
@pytest.fixture
69 71
def local_user():
70
    get_publisher().user_class.wipe()
71 72
    user = get_publisher().user_class()
72 73
    user.name = 'Jean Darmette'
73 74
    user.email = 'jean.darmette@triffouilis.fr'
......
78 79

  
79 80
@pytest.fixture
80 81
def admin_user():
81
    get_publisher().user_class.wipe()
82 82
    user = get_publisher().user_class()
83 83
    user.name = 'John Doe Admin'
84 84
    user.email = 'john.doe@example.com'
......
788 788
    assert 'name' in resp.json['evolution'][1]['who']
789 789

  
790 790

  
791
def test_api_access_restrict_to_anonymised_data(pub, local_user):
791
@pytest.mark.parametrize('http_basic_auth', [False, True])
792
def test_api_access_restrict_to_anonymised_data(pub, local_user, http_basic_auth):
792 793
    pub.role_class.wipe()
793 794
    role = pub.role_class(name='test')
794 795
    role.store()
......
823 824
    access.access_key = '12345'
824 825
    access.store()
825 826

  
826
    resp = get_app(pub).get(
827
        sign_uri(
828
            '/api/forms/test/list?full=on',
829
            user=local_user,
830
            orig=access.access_identifier,
831
            key=access.access_key,
832
        )
833
    )
827
    app = get_app(pub)
828

  
829
    if http_basic_auth:
830
        # there's not "defaults to admin" permissions in case of basic authentication.
831
        access.roles = [role]
832
        access.store()
833

  
834
        def get_url(url, **kwargs):
835
            app.set_authorization(('Basic', ('test', '12345')))
836
            return app.get(url, **kwargs)
837

  
838
    else:
839

  
840
        def get_url(url, **kwargs):
841
            return app.get(
842
                sign_uri(url, user=local_user, orig=access.access_identifier, key=access.access_key), **kwargs
843
            )
844

  
845
    resp = get_url('/api/forms/test/list?full=on')
834 846
    assert len(resp.json) == 10
835 847
    assert resp.json[0]['fields']['foobar'] == 'FOO BAR1'
836 848
    assert resp.json[0]['fields']['foobar2'] == 'FOO BAR 2'
837 849
    assert resp.json[0].get('user')
838 850

  
851
    # get a single formdata
852
    resp = get_url('/api/forms/test/%s/' % formdata.id)
853
    assert 'user' in resp.json
854

  
839 855
    # restrict API access to anonymised data
840 856
    access.restrict_to_anonymised_data = True
841 857
    access.store()
842 858

  
843
    resp = get_app(pub).get(
844
        sign_uri(
845
            '/api/forms/test/list?full=on',
846
            user=local_user,
847
            orig=access.access_identifier,
848
            key=access.access_key,
849
        )
850
    )
859
    resp = get_url('/api/forms/test/list?full=on')
851 860
    assert len(resp.json) == 10
852 861
    assert 'foobar' not in resp.json[0]['fields']
853 862
    assert resp.json[0]['fields']['foobar2'] == 'FOO BAR 2'
854 863
    assert not resp.json[0].get('user')
855 864

  
865
    # get a single formdata
866
    resp = get_url('/api/forms/test/%s/' % formdata.id)
867
    assert 'user' not in resp.json
868

  
869
    if http_basic_auth:
870
        # for basic HTTP authentication, check there's no access if roles are not given.
871
        access.roles = []
872
        access.store()
873

  
874
        get_url('/api/forms/test/list?full=on', status=403)
875
        get_url('/api/forms/test/%s/' % formdata.id, status=403)
876

  
856 877

  
857 878
def test_api_geojson_formdata(pub, local_user):
858 879
    pub.role_class.wipe()
wcs/api.py
189 189
            raise TraversalError()
190 190

  
191 191
    def check_access(self, api_name=None):
192
        if get_request().has_anonymised_data_api_restriction():
193
            if not is_url_signed() or (get_request().user and get_request().user.is_admin):
194
                raise AccessForbiddenError('user not authenticated')
195
        else:
196
            if get_request().user and get_request().user.is_admin:
197
                return  # grant access to admins, to ease debug
198
            api_user = get_user_from_api_query_string(api_name=api_name)
199
            if not api_user:
200
                raise AccessForbiddenError('user not authenticated')
201
            if not self.formdef.is_of_concern_for_user(api_user):
202
                raise AccessForbiddenError('unsufficient roles')
192
        if get_request().user and get_request().user.is_admin:
193
            return  # grant access to admins, to ease debug
194

  
195
        api_user = get_user_from_api_query_string(api_name=api_name)
196

  
197
        if get_request().has_anonymised_data_api_restriction() and is_url_signed():
198
            # when requesting anonymous data, a signature is enough
199
            return
200

  
201
        if not api_user:
202
            raise AccessForbiddenError('user not authenticated')
203
        if not self.formdef.is_of_concern_for_user(api_user):
204
            raise AccessForbiddenError('unsufficient roles')
203 205

  
204 206
    def _q_lookup(self, component):
205 207
        if component == 'ics':
wcs/api_access.py
89 89
            is_api_user = True
90 90
            anonymous = False
91 91

  
92
            def __init__(self, api_access):
93
                self.api_access = api_access
94

  
92 95
            def can_go_in_admin(self):
93 96
                return False
94 97

  
......
98 101
            def get_roles(self):
99 102
                return self.roles
100 103

  
101
        user = RestrictedApiUser()
104
        user = RestrictedApiUser(self)
102 105
        user.roles = [x.id for x in self.get_roles()]
103 106
        return user
104 107

  
wcs/forms/common.py
158 158
        session = get_session()
159 159
        mine = False
160 160
        if api_call:
161
            if get_request().has_anonymised_data_api_restriction():
161
            user = get_user_from_api_query_string() or get_request().user
162
            if get_request().has_anonymised_data_api_restriction() and (not user or not user.is_api_user):
162 163
                if is_url_signed() or (get_request().user and get_request().user.is_admin):
163 164
                    return None
164 165
                else:
165 166
                    raise errors.AccessUnauthorizedError()
166
            else:
167
                user = get_user_from_api_query_string() or get_request().user
168 167
        else:
169 168
            user = get_request().user
170 169
        if user and not user.anonymous:
wcs/qommon/http_request.py
223 223

  
224 224
        if 'anonymise' in self.form:
225 225
            return True
226

  
226 227
        orig = self.form.get('orig')
227 228
        if orig:
228 229
            api_access = ApiAccess.get_by_identifier(orig)
229 230
            if api_access:
230 231
                return api_access.restrict_to_anonymised_data
232

  
233
        if self.user and self.user.is_api_user:
234
            return self.user.api_access.restrict_to_anonymised_data
235

  
231 236
        return False
232 237

  
233 238
    @property
234
-