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

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

from django.contrib.auth.models import Group
7
8
from django.http import HttpResponse
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

LOGGER = logging.getLogger(__name__)
14
CLIENT_DEBUGGING = False
15

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

19

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

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

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

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

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

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def _webpage_client_userid(request):
    username = request.POST.get('username')
    if username.startswith('webpage-client:'):
        components = username.split(':', maxsplit=1)
        if len(components) == 2:
            return components[1]
    return ''

def _webpage_client_session(request):
    query = Session.objects.filter(
        session_key=request.POST.get('password'),
    )
    if query.exists() and len(query) == 1:
        return query.first()
    return None
71

72
73
74
def _webpage_client_valid(request):
    userid = _webpage_client_userid(request)
    session = _webpage_client_session(request)
75

76
77
78
79
80
    if (
            _webpage_client_userid(request) != ''
            and session.get_decoded().get('_auth_user_id') == userid
    ):
        return True
81

Lukas Burgey's avatar
Lukas Burgey committed
82
    #LOGGER.error('Failed to authenticate webpage client for RabbitMQ')
83
    return False
84

85
86
# VIEWS: authentication and authorization for
# apiclients and webpage-clients
87

88
89
def user_endpoint(request):
    if _webpage_client_valid(request):
Lukas Burgey's avatar
Lukas Burgey committed
90
        # LOGGER.info('Authenticated webpage client')
91
        return ALLOW
92

93
94
95
96
    user = authenticate(
        username=request.POST.get('username'),
        password=request.POST.get('password'),
    )
Lukas Burgey's avatar
Lukas Burgey committed
97
    if user is not None:
Lukas Burgey's avatar
Lukas Burgey committed
98
        #LOGGER.info('Authenticated client as %s', user)
99
        return ALLOW
100
101


102
103
    LOGGER.error('Failed to authenticate user for RabbitMQ')
    return DENY
104

105
def vhost_endpoint(request):
106
    # check if on the correct virtual host
107
108
    if _valid_vhost(request) and _valid_user(request):
        return ALLOW
109

110
111
112
113
114
115
116
117
118
    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
119
        and name == 'users'
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
        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
138
        and name in models.RabbitMQInstance.load().exchanges
139
140
141
        and not 'write' in permission
    )

Lukas Burgey's avatar
Lukas Burgey committed
142
143
def resource_auth_decision(request, decision):
    user = request.POST.get('username')
144
    permission = request.POST.get('permission', [])
Lukas Burgey's avatar
Lukas Burgey committed
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
    resource = request.POST.get('resource', '')
    name = request.POST.get('name', '')
    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,
        )
    return decision
164

Lukas Burgey's avatar
Lukas Burgey committed
165
def resource_endpoint(request):
166
167
168
169
170
    if _valid_vhost(request):
        if (
                _webpage_client_userid(request)
                and _resource_authorized_webpage_client(request)
        ):
Lukas Burgey's avatar
Lukas Burgey committed
171
            return resource_auth_decision(request, ALLOW)
172
173
174
175
176

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

Lukas Burgey's avatar
Lukas Burgey committed
179
    return resource_auth_decision(request, DENY)
180

Lukas Burgey's avatar
Lukas Burgey committed
181
182
def topic_auth_decision(request, decision):
    user = request.POST.get('username')
183
184
185
186
187
    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
188
189
190
191
    if decision == ALLOW:
        LOGGER.debug(
            "[topic] ALLOW %s %s %s '%s' for %s",
            permission,
Lukas Burgey's avatar
Lukas Burgey committed
192
193
194
            resource,
            name,
            routing_key,
Lukas Burgey's avatar
Lukas Burgey committed
195
            user,
Lukas Burgey's avatar
Lukas Burgey committed
196
        )
Lukas Burgey's avatar
Lukas Burgey committed
197
    else:
Lukas Burgey's avatar
Lukas Burgey committed
198
        LOGGER.error(
Lukas Burgey's avatar
Lukas Burgey committed
199
200
            "[topic] DENY %s %s %s '%s' for %s",
            permission,
Lukas Burgey's avatar
Lukas Burgey committed
201
202
203
            resource,
            name,
            routing_key,
Lukas Burgey's avatar
Lukas Burgey committed
204
            user,
Lukas Burgey's avatar
Lukas Burgey committed
205
        )
Lukas Burgey's avatar
Lukas Burgey committed
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    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)
220

Lukas Burgey's avatar
Lukas Burgey committed
221
222
223
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
224

Lukas Burgey's avatar
Lukas Burgey committed
225
226
227
228
229
230
    if name == 'services':
        if routing_key.startswith('service.'):
            match = re.search('service.(.+)', routing_key)
            if match:
                service_name = match.group(1)
                if apiclient.site.services.filter(name=service_name).exists():
Lukas Burgey's avatar
Lukas Burgey committed
231
                    return ALLOW
Lukas Burgey's avatar
Lukas Burgey committed
232
233
    elif name == 'sites':
        if routing_key == apiclient.site.name:
Lukas Burgey's avatar
Lukas Burgey committed
234
235
236
            return topic_auth_decision(request, ALLOW)

        return topic_auth_decision(request, DENY)
Lukas Burgey's avatar
Lukas Burgey committed
237
238
239
    elif name == 'groups':
        try:
            group = Group.objects.get(name=routing_key)
Lukas Burgey's avatar
Lukas Burgey committed
240

Lukas Burgey's avatar
Lukas Burgey committed
241
            try:
Lukas Burgey's avatar
Lukas Burgey committed
242
                models.Site.objects.get(
Lukas Burgey's avatar
Lukas Burgey committed
243
244
245
                    services__groups=group,
                    client=apiclient,
                )
Lukas Burgey's avatar
Lukas Burgey committed
246
                return topic_auth_decision(request, ALLOW)
Lukas Burgey's avatar
Lukas Burgey committed
247

Lukas Burgey's avatar
Lukas Burgey committed
248
            except models.Site.MultipleObjectsReturned:
Lukas Burgey's avatar
Lukas Burgey committed
249
                return topic_auth_decision(request, ALLOW)
Lukas Burgey's avatar
Lukas Burgey committed
250

Lukas Burgey's avatar
Lukas Burgey committed
251
            except models.Site.DoesNotExist:
Lukas Burgey's avatar
Lukas Burgey committed
252
                return topic_auth_decision(request, DENY)
Lukas Burgey's avatar
Lukas Burgey committed
253

Lukas Burgey's avatar
Lukas Burgey committed
254
        except Group.DoesNotExist:
Lukas Burgey's avatar
Lukas Burgey committed
255
            return topic_auth_decision(request, DENY)
Lukas Burgey's avatar
Lukas Burgey committed
256

Lukas Burgey's avatar
Lukas Burgey committed
257
    return topic_auth_decision(request, DENY)
Lukas Burgey's avatar
Lukas Burgey committed
258
259
260
261
262
263

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
264
    if webpage_client_userid != '':
Lukas Burgey's avatar
Lukas Burgey committed
265
266
267
268
269
270
        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
271
    return topic_auth_decision(request, DENY)