Statistics
| Branch: | Tag: | Revision:

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

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 django.shortcuts import redirect
38
from django.views.generic.simple import direct_to_template
39
from django.conf import settings
40
from django.core.exceptions import PermissionDenied
41
from django.http import Http404, HttpResponseRedirect
42
from django.core.urlresolvers import reverse
43

    
44
from urllib import unquote
45

    
46
from synnefo.lib.astakos import get_user
47
from synnefo.db.models import VirtualMachine, NetworkInterface, Network
48
from synnefo.lib import astakos
49

    
50
logger = logging.getLogger(__name__)
51

    
52
IP_SEARCH_REGEX = re.compile('([0-9]+)(?:\.[0-9]+){3}')
53
UUID_SEARCH_REGEX = re.compile('([0-9a-z]{8}-([0-9a-z]{4}-){3}[0-9a-z]{12})')
54

    
55
USER_CATALOG_URL = settings.CYCLADES_USER_CATALOG_URL
56

    
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',
75
                           getattr(settings, 'UI_AUTH_COOKIE_NAME',
76
                                   '_pithos2_a'))
77
PERMITTED_GROUPS = getattr(settings, 'HELPDESK_PERMITTED_GROUPS', ['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',
142
                        account_or_ip=account)
143

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

    
147

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

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

    
156
    account_exists = True
157
    vms = []
158
    networks = []
159
    is_ip = IP_SEARCH_REGEX.match(account_or_ip)
160
    is_uuid = UUID_SEARCH_REGEX.match(account_or_ip)
161
    account_name = account_or_ip
162
    auth_token = request.user.get('auth_token')
163

    
164
    if is_ip:
165
        try:
166
            nic = NetworkInterface.objects.get(ipv4=account_or_ip)
167
            account_or_ip = nic.machine.userid
168
            is_uuid = True
169
        except NetworkInterface.DoesNotExist:
170
            account_exists = False
171

    
172
    if is_uuid:
173
        account = account_or_ip
174
        account_name = astakos.get_displayname(auth_token, account,
175
                                               USER_CATALOG_URL)
176
    else:
177
        account_name = account_or_ip
178
        account = astakos.get_user_uuid(auth_token, account_name,
179
                                        USER_CATALOG_URL)
180

    
181
    filter_extra = {}
182
    if not show_deleted:
183
        filter_extra['deleted'] = False
184

    
185
    # all user vms
186
    vms = VirtualMachine.objects.filter(userid=account,
187
                                        **filter_extra).order_by('deleted')
188
    # return all user private and public networks
189
    public_networks = Network.objects.filter(public=True,
190
                                             nics__machine__userid=account,
191
                                             **filter_extra
192
                                             ).order_by('state').distinct()
193
    private_networks = Network.objects.filter(userid=account,
194
                                              **filter_extra).order_by('state')
195
    networks = list(public_networks) + list(private_networks)
196

    
197
    if vms.count() == 0 and private_networks.count() == 0:
198
        account_exists = False
199

    
200
    user_context = {
201
        'account_exists': account_exists,
202
        'is_ip': is_ip,
203
        'account': account,
204
        'vms': vms,
205
        'show_deleted': show_deleted,
206
        'account_name': account_name,
207
        'csrf_token': request.user['auth_token'],
208
        'networks': networks,
209
        'UI_MEDIA_URL': settings.UI_MEDIA_URL
210
    }
211

    
212
    return direct_to_template(request, "helpdesk/account.html",
213
                              extra_context=user_context)
214

    
215

    
216
@helpdesk_user_required
217
@token_check
218
def suspend_vm(request, vm_id):
219
    vm = VirtualMachine.objects.get(pk=vm_id)
220
    vm.suspended = True
221
    vm.save()
222
    account = vm.userid
223
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))
224

    
225

    
226
@helpdesk_user_required
227
@token_check
228
def suspend_vm_release(request, vm_id):
229
    vm = VirtualMachine.objects.get(pk=vm_id)
230
    vm.suspended = False
231
    vm.save()
232
    account = vm.userid
233
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))