clients.py 7.49 KB
Newer Older
Lukas Burgey's avatar
Lukas Burgey committed
1
# pylint: disable=too-many-return-statements
2
3

import logging
Lukas Burgey's avatar
Lukas Burgey committed
4
5

from django.contrib.auth.models import Group
6
from django.http import HttpResponse
Lukas Burgey's avatar
Lukas Burgey committed
7
from django.conf import settings
8
from django.contrib.auth import authenticate
9
from django.contrib.sessions.models import Session
Lukas Burgey's avatar
Lukas Burgey committed
10

Lukas Burgey's avatar
Lukas Burgey committed
11
from .... import models
12
13
14

LOGGER = logging.getLogger(__name__)

15
16
17
ALLOW = HttpResponse('allow')
DENY = HttpResponse('deny')

18

19
def _valid_vhost(request):
Lukas Burgey's avatar
Lukas Burgey committed
20
    if request.POST.get('vhost') == models.RabbitMQInstance.load().vhost:
21
22
23
24
        return True
    LOGGER.error('illegal vhost requested')
    return False

25
def _valid_permission(request):
26
27
    perm = request.POST.get('permission')
    if  perm != 'write':
28
        return True
29
    LOGGER.error('illegal permission requested %s', perm)
30
31
    return False

32
def _valid_user(request):
33
34
35
    return _apiclient_valid(request) or _webpage_client_userid(request)

def _apiclient_valid(request):
Lukas Burgey's avatar
Lukas Burgey committed
36
    valid = models.User.objects.filter(
37
38
39
40
41
        user_type='apiclient',
        username=request.POST.get('username'),
    ).exists()
    if valid:
        return True
42
43
    return False

44
def _apiclient_get(request):
Lukas Burgey's avatar
Lukas Burgey committed
45
    user = models.User.objects.filter(
46
47
48
49
50
51
        user_type='apiclient',
    ).get(
        username=request.POST.get('username'),
    )
    if user:
        return user
52
    LOGGER.error('unable to get user for request')
53
54
    return None

55
def _webpage_client_userid(request):
Lukas Burgey's avatar
Lukas Burgey committed
56
    userid = ''
57
58
59
60
    username = request.POST.get('username')
    if username.startswith('webpage-client:'):
        components = username.split(':', maxsplit=1)
        if len(components) == 2:
Lukas Burgey's avatar
Lukas Burgey committed
61
62
            userid = components[1]
    return userid
63

64
65
def _webpage_client_valid(request):
    userid = _webpage_client_userid(request)
Lukas Burgey's avatar
Lukas Burgey committed
66
67
68
69
70
71
72
73
74
    try:
        session = Session.objects.get(
            session_key=request.POST.get('password'),
        )
        return session.get_decoded().get('_auth_user_id') == userid

    except Session.DoesNotExist:
        LOGGER.info("User %s has no session", userid)
        return False
75

76
77
# VIEWS: authentication and authorization for
# apiclients and webpage-clients
78

79
80
def user_endpoint(request):
    if _webpage_client_valid(request):
Lukas Burgey's avatar
Lukas Burgey committed
81
        # LOGGER.info('Authenticated webpage client')
82
        return ALLOW
83

84
85
86
87
    user = authenticate(
        username=request.POST.get('username'),
        password=request.POST.get('password'),
    )
Lukas Burgey's avatar
Lukas Burgey committed
88
    if user is not None:
Lukas Burgey's avatar
Lukas Burgey committed
89
        #LOGGER.info('Authenticated client as %s', user)
90
        return ALLOW
91
92


93
94
    LOGGER.error('Failed to authenticate user for RabbitMQ')
    return DENY
95

96
def vhost_endpoint(request):
97
    # check if on the correct virtual host
98
99
    if _valid_vhost(request) and _valid_user(request):
        return ALLOW
100

101
102
103
104
105
106
107
108
109
    LOGGER.error('Authorization check for vhost failed for %s', request.POST)
    return DENY

def _resource_authorized_webpage_client(request):
    resource = request.POST.get('resource')
    name = request.POST.get('name', '')
    permission = request.POST.get('permission', [])
    return (
        resource == 'exchange'
Lukas Burgey's avatar
Lukas Burgey committed
110
        and name == 'users'
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
        and not 'write' in permission
    ) or (
        resource == 'queue'
        and name.startswith('stomp-subscription-')
    ) or (
        resource == 'topic'
        and name == _webpage_client_userid(request)
    )

def _resource_authorized_apiclient(request):
    resource = request.POST.get('resource')
    name = request.POST.get('name', '')
    permission = request.POST.get('permission', [])
    return (
        resource == 'queue'
        and name.startswith('amq.gen-')
    ) or (
        resource == 'exchange'
Lukas Burgey's avatar
Lukas Burgey committed
129
        and name in models.RabbitMQInstance.load().exchanges
130
131
132
        and not 'write' in permission
    )

Lukas Burgey's avatar
Lukas Burgey committed
133
134
def resource_auth_decision(request, decision):
    user = request.POST.get('username')
135
    permission = request.POST.get('permission', [])
Lukas Burgey's avatar
Lukas Burgey committed
136
137
    resource = request.POST.get('resource', '')
    name = request.POST.get('name', '')
Lukas Burgey's avatar
Lukas Burgey committed
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
    if settings.DEBUG_AUTH:
        if decision == ALLOW:
            LOGGER.debug(
                "[resource] ALLOW %s %s '%s' for %s",
                permission,
                resource,
                name,
                user,
            )
        else:
            LOGGER.error(
                "[recource] DENY %s %s '%s' for %s",
                permission,
                resource,
                name,
                user,
            )
Lukas Burgey's avatar
Lukas Burgey committed
155
    return decision
156

Lukas Burgey's avatar
Lukas Burgey committed
157
def resource_endpoint(request):
158
159
160
161
162
    if _valid_vhost(request):
        if (
                _webpage_client_userid(request)
                and _resource_authorized_webpage_client(request)
        ):
Lukas Burgey's avatar
Lukas Burgey committed
163
            return resource_auth_decision(request, ALLOW)
164
165
166
167
168

        if (
                _apiclient_valid(request)
                and _resource_authorized_apiclient(request)
        ):
Lukas Burgey's avatar
Lukas Burgey committed
169
            return resource_auth_decision(request, ALLOW)
170

Lukas Burgey's avatar
Lukas Burgey committed
171
    return resource_auth_decision(request, DENY)
172

Lukas Burgey's avatar
Lukas Burgey committed
173
174
def topic_auth_decision(request, decision):
    user = request.POST.get('username')
175
176
177
178
179
    permission = request.POST.get('permission', [])
    resource = request.POST.get('resource', '')
    name = request.POST.get('name', '')
    routing_key = request.POST.get('routing_key', '')

Lukas Burgey's avatar
Lukas Burgey committed
180
181
182
    if not AUTH_DEBUGGING:
        return decision

Lukas Burgey's avatar
Lukas Burgey committed
183
184
185
186
    if decision == ALLOW:
        LOGGER.debug(
            "[topic] ALLOW %s %s %s '%s' for %s",
            permission,
Lukas Burgey's avatar
Lukas Burgey committed
187
188
189
            resource,
            name,
            routing_key,
Lukas Burgey's avatar
Lukas Burgey committed
190
            user,
Lukas Burgey's avatar
Lukas Burgey committed
191
        )
Lukas Burgey's avatar
Lukas Burgey committed
192
    else:
Lukas Burgey's avatar
Lukas Burgey committed
193
        LOGGER.error(
Lukas Burgey's avatar
Lukas Burgey committed
194
195
            "[topic] DENY %s %s %s '%s' for %s",
            permission,
Lukas Burgey's avatar
Lukas Burgey committed
196
197
198
            resource,
            name,
            routing_key,
Lukas Burgey's avatar
Lukas Burgey committed
199
            user,
Lukas Burgey's avatar
Lukas Burgey committed
200
        )
Lukas Burgey's avatar
Lukas Burgey committed
201
202
203
204
205
206
207
208
209
210
211
212
213
214
    return decision

def topic_endpoint_webpageclient(request, webpage_client_userid):
    permission = request.POST.get('permission', [])

    try:
        models.User.objects.get(id=webpage_client_userid)

        if not 'write' in permission:
            return topic_auth_decision(request, ALLOW)
        return topic_auth_decision(request, DENY)

    except models.User.DoesNotExist:
        return topic_auth_decision(request, DENY)
215

Lukas Burgey's avatar
Lukas Burgey committed
216
217
218
def topic_endpoint_apiclient(request, apiclient):
    name = request.POST.get('name', '')
    routing_key = request.POST.get('routing_key', '')
Lukas Burgey's avatar
Lukas Burgey committed
219

Lukas Burgey's avatar
Lukas Burgey committed
220
    if name == 'services':
Lukas Burgey's avatar
Lukas Burgey committed
221
222
223
        # TODO is this check sufficient?
        if apiclient.site.services.filter(name=routing_key).exists():
            return ALLOW
Lukas Burgey's avatar
Lukas Burgey committed
224
225
    elif name == 'sites':
        if routing_key == apiclient.site.name:
Lukas Burgey's avatar
Lukas Burgey committed
226
227
228
            return topic_auth_decision(request, ALLOW)

        return topic_auth_decision(request, DENY)
Lukas Burgey's avatar
Lukas Burgey committed
229
230
231
    elif name == 'groups':
        try:
            group = Group.objects.get(name=routing_key)
Lukas Burgey's avatar
Lukas Burgey committed
232

Lukas Burgey's avatar
Lukas Burgey committed
233
            try:
Lukas Burgey's avatar
Lukas Burgey committed
234
                models.Site.objects.get(
Lukas Burgey's avatar
Lukas Burgey committed
235
236
237
                    services__groups=group,
                    client=apiclient,
                )
Lukas Burgey's avatar
Lukas Burgey committed
238
                return topic_auth_decision(request, ALLOW)
Lukas Burgey's avatar
Lukas Burgey committed
239

Lukas Burgey's avatar
Lukas Burgey committed
240
            except models.Site.MultipleObjectsReturned:
Lukas Burgey's avatar
Lukas Burgey committed
241
                return topic_auth_decision(request, ALLOW)
Lukas Burgey's avatar
Lukas Burgey committed
242

Lukas Burgey's avatar
Lukas Burgey committed
243
            except models.Site.DoesNotExist:
Lukas Burgey's avatar
Lukas Burgey committed
244
                return topic_auth_decision(request, DENY)
Lukas Burgey's avatar
Lukas Burgey committed
245

Lukas Burgey's avatar
Lukas Burgey committed
246
        except Group.DoesNotExist:
Lukas Burgey's avatar
Lukas Burgey committed
247
            return topic_auth_decision(request, DENY)
Lukas Burgey's avatar
Lukas Burgey committed
248

Lukas Burgey's avatar
Lukas Burgey committed
249
    return topic_auth_decision(request, DENY)
Lukas Burgey's avatar
Lukas Burgey committed
250
251
252
253
254
255

def topic_endpoint(request):
    if not _valid_vhost(request) or not _valid_permission(request):
        return DENY

    webpage_client_userid = _webpage_client_userid(request)
Lukas Burgey's avatar
Lukas Burgey committed
256
    if webpage_client_userid != '':
Lukas Burgey's avatar
Lukas Burgey committed
257
258
259
260
261
262
        return topic_endpoint_webpageclient(request, webpage_client_userid)

    apiclient = _apiclient_get(request)
    if apiclient:
        return topic_endpoint_apiclient(request, apiclient)

Lukas Burgey's avatar
Lukas Burgey committed
263
    return topic_auth_decision(request, DENY)