To receive notifications about scheduled maintenance, please subscribe to the mailing-list gitlab-operations@sympa.ethz.ch. You can subscribe to the mailing-list at https://sympa.ethz.ch

test_security.py 6.03 KB
Newer Older
1
"""Tests for all security function."""
Alexander Dietmüller's avatar
Alexander Dietmüller committed
2
3
4
5

import pytest


6
7
ALL_RESOURCES = ['lectures', 'courses', 'signups', 'selections', 'payments']
ADMIN_RESOURCES = ['lectures', 'courses']  # only admin can write
8
PERSONAL_RESOURCES = ['signups', 'selections']  # users can only see their own
9
10
11


@pytest.mark.parametrize('resource', ALL_RESOURCES)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
12
13
14
15
16
17
@pytest.mark.parametrize('method', ['get', 'post'])
def test_auth_required_for_resource(app, resource, method):
    """Without auth header, we get get 401 for all methods."""
    getattr(app.client, method)('/' + resource, data={}, assert_status=401)


18
@pytest.mark.parametrize('resource', ALL_RESOURCES)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
19
20
21
22
23
24
25
26
27
28
29
30
@pytest.mark.parametrize('method', ['get', 'patch', 'delete'])
def test_auth_required_for_item(app, resource, method):
    """Without auth provided, we can access any item either."""
    # Bypass validation and put a empty item directly into db
    with app.app_context():
        _id = app.data.driver.db[resource].insert({})

    getattr(app.client, method)('/%s/%s' % (resource, _id),
                                data={},
                                assert_status=401)


31
@pytest.mark.parametrize('resource', ADMIN_RESOURCES)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
32
33
34
35
36
37
def test_user_can_read(app, resource):
    """Users should be able to to GET requests on resource and item.

    Not signups! There, users can only see their own items -> extra test
    This implies that admins can read, too, since every admin is a user.
    """
38
    with app.user():
Alexander Dietmüller's avatar
Alexander Dietmüller committed
39
        # Read resource
40
        app.client.get('/' + resource, assert_status=200)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
41
42
43
44
45
46
47

        # Create fake item and read item
        _id = app.data.driver.db[resource].insert({})
        app.client.get('/%s/%s' % (resource, _id),
                       assert_status=200)


48
@pytest.mark.parametrize('resource', ADMIN_RESOURCES)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
49
50
def test_user_cannot_write(app, resource):
    """Users cannot create, modify or delete."""
51
    with app.user():
Alexander Dietmüller's avatar
Alexander Dietmüller committed
52
53
        data = {}

54
        # Try to post something
Alexander Dietmüller's avatar
Alexander Dietmüller committed
55
56
        app.client.post('/' + resource,
                        data=data,
57
                        assert_status=403)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
58
59
60
61
62

        # Create fake item, try to patch/delete it
        _id = app.data.driver.db[resource].insert({})
        app.client.patch('/%s/%s' % (resource, _id),
                         data=data,
63
                         assert_status=403)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
64
        app.client.delete('/%s/%s' % (resource, _id),
65
                          assert_status=403)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
66
67
68
69


def test_signup_with_own_nethz_only(app):
    """Users can only post singups with their own nethz."""
70
71
    nethz = 'Something'
    with app.user(nethz=nethz):
Alexander Dietmüller's avatar
Alexander Dietmüller committed
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
        # Create fake course to sign up to
        course = str(app.data.driver.db['courses'].insert({}))

        # Try with other nethz
        bad_signup = {
            'nethz': 'Notthenethz',
            'course': course
        }
        app.client.post('/signups',
                        data=bad_signup,
                        assert_status=422)

        # Try with own nethz
        good_signup = {
            'nethz': nethz,
            'course': course
        }
        app.client.post('/signups',
                        data=good_signup,
91
92
93
                        assert_status=201)


94
def test_selection_own_nethz(app):
95
96
97
98
99
100
101
102
103
    """Users can only select courses for themselves."""
    nethz = 'Something'
    with app.user(nethz=nethz):
        # Create fake course to select
        course = str(app.data.driver.db['courses'].insert({}))

        # Try with other nethz
        bad_selection = {
            'nethz': 'Notthenethz',
104
            'course': course,
105
106
107
108
109
110
111
112
        }
        app.client.post('/selections',
                        data=bad_selection,
                        assert_status=422)

        # Try with own nethz
        good_selection = {
            'nethz': nethz,
113
            'course': course,
114
115
116
        }
        app.client.post('/selections',
                        data=good_selection,
Alexander Dietmüller's avatar
Alexander Dietmüller committed
117
118
119
                        assert_status=201)


120
121
122
@pytest.mark.parametrize('resource', PERSONAL_RESOURCES)
def test_user_visibility(app, resource):
    """Test that a user cannot see others' signups or selections."""
123
124
    nethz = 'Something'
    with app.user(nethz=nethz):
Alexander Dietmüller's avatar
Alexander Dietmüller committed
125
        # Create fake signup with different nethz
126
127
        own = str(app.data.driver.db[resource].insert({'nethz': nethz}))
        other = str(app.data.driver.db[resource].insert({'nethz': 'trolo'}))
Alexander Dietmüller's avatar
Alexander Dietmüller committed
128
129

        # Resource: Can only see own, not both signups
130
        response = app.client.get('/' + resource, assert_status=200)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
131
        assert len(response['_items']) == 1
132
        assert response['_items'][0]['nethz'] == nethz
Alexander Dietmüller's avatar
Alexander Dietmüller committed
133
134

        # Items
135
136
        own_url = '/%s/%s' % (resource, own)
        other_url = '/%s/%s' % (resource, other)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
137
138

        # Get
139
140
        app.client.get(own_url, assert_status=200)
        app.client.get(other_url, assert_status=404)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
141

142
143
144
        # Patch (if we can see item, we get 428 since etag is missing)
        app.client.patch(own_url, data={}, assert_status=428)
        app.client.patch(other_url, data={}, assert_status=404)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
145
146

        # Delete (etag missing again)
147
148
        app.client.delete(own_url, assert_status=428)
        app.client.delete(other_url, assert_status=404)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
149

150

151
152
153
154
@pytest.mark.parametrize('resource', PERSONAL_RESOURCES)
def test_admin_signup_visibility(app, resource):
    """Test that we an admin can see others' signups and selections."""
    with app.admin(nethz='somethingsomething'):
155
        headers = {'If-Match': 'Wrong'}
Alexander Dietmüller's avatar
Alexander Dietmüller committed
156
157

        # Create fake signup with different nethz
158
        other = str(app.data.driver.db[resource].insert({'nethz': 'trolo'}))
Alexander Dietmüller's avatar
Alexander Dietmüller committed
159
160

        # Resource: Can see signups
161
        response = app.client.get('/' + resource,
162
163
                                  headers=headers,
                                  assert_status=200)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
164
165
166
        assert len(response['_items']) == 1

        # Items
167
        url = '/%s/%s' % (resource, other)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
168
169

        # Get
170
        app.client.get(url, headers=headers, assert_status=200)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
171
172

        # Patch (if we can see item, we get 412 since etag is wrong)
173
        app.client.patch(url, headers=headers, data={}, assert_status=412)
Alexander Dietmüller's avatar
Alexander Dietmüller committed
174
175

        # Delete (etag missing again)
176
        app.client.delete(url, headers=headers, assert_status=412)