Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / helpdesk / views.py @ 605d23bf

History | View | Annotate | Download (7.6 kB)

1
# Copyright 2012 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import re
35
import logging
36

    
37
from itertools import chain
38

    
39
from django.shortcuts import redirect, get_object_or_404
40
from django.views.generic.simple import direct_to_template
41
from django.db.models import get_apps
42
from django.conf import settings
43
from django.core.exceptions import PermissionDenied
44
from django.db.models import Q
45
from django.http import Http404, HttpResponse, HttpResponseRedirect
46
from django.utils import simplejson as json
47
from django.core.urlresolvers import reverse
48

    
49
from urllib import unquote
50

    
51
from synnefo.lib.astakos import get_user
52
from synnefo.db.models import *
53

    
54
logger = logging.getLogger(__name__)
55

    
56
IP_SEARCH_REGEX = re.compile('([0-9]+)(?:\.[0-9]+){3}')
57

    
58
def get_token_from_cookie(request, cookiename):
59
    """
60
    Extract token from the cookie name provided. Cookie should be in the same
61
    form as astakos service sets its cookie contents::
62

63
        <user_uniq>|<user_token>
64
    """
65
    try:
66
        cookie_content = unquote(request.COOKIES.get(cookiename, None))
67
        return cookie_content.split("|")[1]
68
    except AttributeError:
69
        pass
70

    
71
    return None
72

    
73

    
74
AUTH_COOKIE_NAME = getattr(settings, 'HELPDESK_AUTH_COOKIE_NAME', getattr(settings,
75
    'UI_AUTH_COOKIE_NAME', '_pithos2_a'))
76
PERMITTED_GROUPS = getattr(settings, 'HELPDESK_PERMITTED_GROUPS',
77
                                    ['helpdesk'])
78
SHOW_DELETED_VMS = getattr(settings, 'HELPDESK_SHOW_DELETED_VMS', False)
79

    
80

    
81
def token_check(func):
82
    """
83
    Mimic csrf security check using user auth token.
84
    """
85
    def wrapper(request, *args, **kwargs):
86
        if not hasattr(request, 'user'):
87
            raise PermissionDenied
88

    
89
        token = request.POST.get('token', None)
90
        if token and token != request.user.get('auth_token', None):
91
            return func(request, *args, **kwargs)
92

    
93
        raise PermissionDenied
94

    
95
    return wrapper
96

    
97

    
98
def helpdesk_user_required(func, permitted_groups=PERMITTED_GROUPS):
99
    """
100
    Django view wrapper that checks if identified request user has helpdesk
101
    permissions (exists in helpdesk group)
102
    """
103
    def wrapper(request, *args, **kwargs):
104
        HELPDESK_ENABLED = getattr(settings, 'HELPDESK_ENABLED', True)
105
        if not HELPDESK_ENABLED:
106
            raise Http404
107

    
108
        token = get_token_from_cookie(request, AUTH_COOKIE_NAME)
109
        get_user(request, settings.ASTAKOS_URL, fallback_token=token)
110
        if hasattr(request, 'user') and request.user:
111
            groups = request.user.get('groups', [])
112

    
113
            if not groups:
114
                raise PermissionDenied
115

    
116
            has_perm = False
117
            for g in groups:
118
                if g in permitted_groups:
119
                    has_perm = True
120

    
121
            if not has_perm:
122
                raise PermissionDenied
123
        else:
124
            raise PermissionDenied
125

    
126
        logging.debug("User %s accessed helpdesk view" % (request.user_uniq))
127
        return func(request, *args, **kwargs)
128

    
129
    return wrapper
130

    
131

    
132
@helpdesk_user_required
133
def index(request):
134
    """
135
    Helpdesk index view.
136
    """
137

    
138
    # if form submitted redirect to details
139
    account = request.GET.get('account', None)
140
    if account:
141
        return redirect('synnefo.helpdesk.views.account', account_or_ip=account)
142

    
143
    # show index template
144
    return direct_to_template(request, "helpdesk/index.html")
145

    
146

    
147
@helpdesk_user_required
148
def account(request, account_or_ip):
149
    """
150
    Account details view.
151
    """
152

    
153
    show_deleted = bool(int(request.GET.get('deleted', SHOW_DELETED_VMS)))
154

    
155
    account_exists = True
156
    vms = []
157
    networks = []
158
    is_ip = IP_SEARCH_REGEX.match(account_or_ip)
159
    account = account_or_ip
160

    
161
    if is_ip:
162
        try:
163
            nic = NetworkInterface.objects.get(ipv4=account_or_ip)
164
            account = nic.machine.userid
165
        except NetworkInterface.DoesNotExist:
166
            account_exists = False
167
    else:
168
        filter_extra = {}
169
        if not show_deleted:
170
            filter_extra['deleted'] = False
171

    
172
        # all user vms
173
        vms = VirtualMachine.objects.filter(userid=account,
174
                                            **filter_extra).order_by('deleted')
175
        # return all user private and public networks
176
        public_networks = Network.objects.filter(public=True,
177
                                                 **filter_extra).order_by('state')
178
        private_networks = Network.objects.filter(userid=account,
179
                                                 **filter_extra).order_by('state')
180
        networks = list(public_networks) + list(private_networks)
181

    
182
        if vms.count() == 0 and private_networks.count() == 0:
183
            account_exists = False
184

    
185
    user_context = {
186
        'account_exists': account_exists,
187
        'is_ip': is_ip,
188
        'account': account,
189
        'vms': vms,
190
        'csrf_token': request.user['auth_token'],
191
        'networks': networks,
192
        'UI_MEDIA_URL': settings.UI_MEDIA_URL
193
    }
194

    
195
    return direct_to_template(request, "helpdesk/account.html",
196
        extra_context=user_context)
197

    
198

    
199
@helpdesk_user_required
200
@token_check
201
def suspend_vm(request, vm_id):
202
    vm = VirtualMachine.objects.get(pk=vm_id)
203
    vm.suspended = True
204
    vm.save()
205
    account = vm.userid
206
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))
207

    
208

    
209
@helpdesk_user_required
210
@token_check
211
def suspend_vm_release(request, vm_id):
212
    vm = VirtualMachine.objects.get(pk=vm_id)
213
    vm.suspended = False
214
    vm.save()
215
    account = vm.userid
216
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))
217

    
218

    
219
@helpdesk_user_required
220
def user_list(request):
221
    """
222
    Return a json list of users based on the prefix provided. Prefix
223
    should end with "@".
224
    """
225

    
226
    prefix = request.GET.get('prefix', None)
227
    if not prefix or "@" not in prefix:
228
        raise Http404
229

    
230
    # keep only the user part (e.g. "user@")
231
    prefix = prefix.split("@")[0] + "@"
232

    
233
    q = Q(userid__startswith=prefix) & ~Q(userid=None)
234
    vm_users = VirtualMachine.objects.filter(q).values_list("userid", flat=True)
235
    net_users = Network.objects.filter(q).values_list("userid", flat=True)
236
    users = list(set(list(vm_users) + list(net_users)))
237
    return HttpResponse(json.dumps(users), content_type="application/json")
238