Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (7.6 kB)

1 605d23bf Kostas Papadimitriou
# Copyright 2012 GRNET S.A. All rights reserved.
2 605d23bf Kostas Papadimitriou
#
3 605d23bf Kostas Papadimitriou
# Redistribution and use in source and binary forms, with or
4 605d23bf Kostas Papadimitriou
# without modification, are permitted provided that the following
5 605d23bf Kostas Papadimitriou
# conditions are met:
6 605d23bf Kostas Papadimitriou
#
7 605d23bf Kostas Papadimitriou
#   1. Redistributions of source code must retain the above
8 605d23bf Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
9 605d23bf Kostas Papadimitriou
#      disclaimer.
10 605d23bf Kostas Papadimitriou
#
11 605d23bf Kostas Papadimitriou
#   2. Redistributions in binary form must reproduce the above
12 605d23bf Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
13 605d23bf Kostas Papadimitriou
#      disclaimer in the documentation and/or other materials
14 605d23bf Kostas Papadimitriou
#      provided with the distribution.
15 605d23bf Kostas Papadimitriou
#
16 605d23bf Kostas Papadimitriou
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 605d23bf Kostas Papadimitriou
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 605d23bf Kostas Papadimitriou
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 605d23bf Kostas Papadimitriou
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 605d23bf Kostas Papadimitriou
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 605d23bf Kostas Papadimitriou
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 605d23bf Kostas Papadimitriou
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 605d23bf Kostas Papadimitriou
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 605d23bf Kostas Papadimitriou
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 605d23bf Kostas Papadimitriou
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 605d23bf Kostas Papadimitriou
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 605d23bf Kostas Papadimitriou
# POSSIBILITY OF SUCH DAMAGE.
28 605d23bf Kostas Papadimitriou
#
29 605d23bf Kostas Papadimitriou
# The views and conclusions contained in the software and
30 605d23bf Kostas Papadimitriou
# documentation are those of the authors and should not be
31 605d23bf Kostas Papadimitriou
# interpreted as representing official policies, either expressed
32 605d23bf Kostas Papadimitriou
# or implied, of GRNET S.A.
33 605d23bf Kostas Papadimitriou
34 d59d86d4 Kostas Papadimitriou
import re
35 605d23bf Kostas Papadimitriou
import logging
36 d59d86d4 Kostas Papadimitriou
37 9e3519e0 Kostas Papadimitriou
from itertools import chain
38 9e3519e0 Kostas Papadimitriou
39 d59d86d4 Kostas Papadimitriou
from django.shortcuts import redirect, get_object_or_404
40 c3564ce9 Kostas Papadimitriou
from django.views.generic.simple import direct_to_template
41 e129e26b Kostas Papadimitriou
from django.db.models import get_apps
42 e129e26b Kostas Papadimitriou
from django.conf import settings
43 e129e26b Kostas Papadimitriou
from django.core.exceptions import PermissionDenied
44 e129e26b Kostas Papadimitriou
from django.db.models import Q
45 605d23bf Kostas Papadimitriou
from django.http import Http404, HttpResponse, HttpResponseRedirect
46 9e3519e0 Kostas Papadimitriou
from django.utils import simplejson as json
47 605d23bf Kostas Papadimitriou
from django.core.urlresolvers import reverse
48 605d23bf Kostas Papadimitriou
49 ebda2ad4 Kostas Papadimitriou
from urllib import unquote
50 c3564ce9 Kostas Papadimitriou
51 ebda2ad4 Kostas Papadimitriou
from synnefo.lib.astakos import get_user
52 9e3519e0 Kostas Papadimitriou
from synnefo.db.models import *
53 e129e26b Kostas Papadimitriou
54 605d23bf Kostas Papadimitriou
logger = logging.getLogger(__name__)
55 605d23bf Kostas Papadimitriou
56 d59d86d4 Kostas Papadimitriou
IP_SEARCH_REGEX = re.compile('([0-9]+)(?:\.[0-9]+){3}')
57 d59d86d4 Kostas Papadimitriou
58 ebda2ad4 Kostas Papadimitriou
def get_token_from_cookie(request, cookiename):
59 ebda2ad4 Kostas Papadimitriou
    """
60 ebda2ad4 Kostas Papadimitriou
    Extract token from the cookie name provided. Cookie should be in the same
61 ebda2ad4 Kostas Papadimitriou
    form as astakos service sets its cookie contents::
62 ebda2ad4 Kostas Papadimitriou

63 ebda2ad4 Kostas Papadimitriou
        <user_uniq>|<user_token>
64 ebda2ad4 Kostas Papadimitriou
    """
65 ebda2ad4 Kostas Papadimitriou
    try:
66 ebda2ad4 Kostas Papadimitriou
        cookie_content = unquote(request.COOKIES.get(cookiename, None))
67 ebda2ad4 Kostas Papadimitriou
        return cookie_content.split("|")[1]
68 ebda2ad4 Kostas Papadimitriou
    except AttributeError:
69 ebda2ad4 Kostas Papadimitriou
        pass
70 ebda2ad4 Kostas Papadimitriou
71 ebda2ad4 Kostas Papadimitriou
    return None
72 ebda2ad4 Kostas Papadimitriou
73 9fd36718 Kostas Papadimitriou
74 605d23bf Kostas Papadimitriou
AUTH_COOKIE_NAME = getattr(settings, 'HELPDESK_AUTH_COOKIE_NAME', getattr(settings,
75 605d23bf Kostas Papadimitriou
    'UI_AUTH_COOKIE_NAME', '_pithos2_a'))
76 605d23bf Kostas Papadimitriou
PERMITTED_GROUPS = getattr(settings, 'HELPDESK_PERMITTED_GROUPS',
77 605d23bf Kostas Papadimitriou
                                    ['helpdesk'])
78 605d23bf Kostas Papadimitriou
SHOW_DELETED_VMS = getattr(settings, 'HELPDESK_SHOW_DELETED_VMS', False)
79 605d23bf Kostas Papadimitriou
80 605d23bf Kostas Papadimitriou
81 605d23bf Kostas Papadimitriou
def token_check(func):
82 605d23bf Kostas Papadimitriou
    """
83 605d23bf Kostas Papadimitriou
    Mimic csrf security check using user auth token.
84 605d23bf Kostas Papadimitriou
    """
85 605d23bf Kostas Papadimitriou
    def wrapper(request, *args, **kwargs):
86 605d23bf Kostas Papadimitriou
        if not hasattr(request, 'user'):
87 605d23bf Kostas Papadimitriou
            raise PermissionDenied
88 605d23bf Kostas Papadimitriou
89 605d23bf Kostas Papadimitriou
        token = request.POST.get('token', None)
90 605d23bf Kostas Papadimitriou
        if token and token != request.user.get('auth_token', None):
91 605d23bf Kostas Papadimitriou
            return func(request, *args, **kwargs)
92 605d23bf Kostas Papadimitriou
93 605d23bf Kostas Papadimitriou
        raise PermissionDenied
94 605d23bf Kostas Papadimitriou
95 605d23bf Kostas Papadimitriou
    return wrapper
96 9fd36718 Kostas Papadimitriou
97 e129e26b Kostas Papadimitriou
98 605d23bf Kostas Papadimitriou
def helpdesk_user_required(func, permitted_groups=PERMITTED_GROUPS):
99 e129e26b Kostas Papadimitriou
    """
100 e129e26b Kostas Papadimitriou
    Django view wrapper that checks if identified request user has helpdesk
101 e129e26b Kostas Papadimitriou
    permissions (exists in helpdesk group)
102 e129e26b Kostas Papadimitriou
    """
103 e129e26b Kostas Papadimitriou
    def wrapper(request, *args, **kwargs):
104 9fd36718 Kostas Papadimitriou
        HELPDESK_ENABLED = getattr(settings, 'HELPDESK_ENABLED', True)
105 9fd36718 Kostas Papadimitriou
        if not HELPDESK_ENABLED:
106 9fd36718 Kostas Papadimitriou
            raise Http404
107 9fd36718 Kostas Papadimitriou
108 605d23bf Kostas Papadimitriou
        token = get_token_from_cookie(request, AUTH_COOKIE_NAME)
109 e129e26b Kostas Papadimitriou
        get_user(request, settings.ASTAKOS_URL, fallback_token=token)
110 9e3519e0 Kostas Papadimitriou
        if hasattr(request, 'user') and request.user:
111 e129e26b Kostas Papadimitriou
            groups = request.user.get('groups', [])
112 0e5c88d0 Kostas Papadimitriou
113 0e5c88d0 Kostas Papadimitriou
            if not groups:
114 0e5c88d0 Kostas Papadimitriou
                raise PermissionDenied
115 0e5c88d0 Kostas Papadimitriou
116 605d23bf Kostas Papadimitriou
            has_perm = False
117 e129e26b Kostas Papadimitriou
            for g in groups:
118 605d23bf Kostas Papadimitriou
                if g in permitted_groups:
119 605d23bf Kostas Papadimitriou
                    has_perm = True
120 605d23bf Kostas Papadimitriou
121 605d23bf Kostas Papadimitriou
            if not has_perm:
122 605d23bf Kostas Papadimitriou
                raise PermissionDenied
123 e129e26b Kostas Papadimitriou
        else:
124 e129e26b Kostas Papadimitriou
            raise PermissionDenied
125 e129e26b Kostas Papadimitriou
126 605d23bf Kostas Papadimitriou
        logging.debug("User %s accessed helpdesk view" % (request.user_uniq))
127 e129e26b Kostas Papadimitriou
        return func(request, *args, **kwargs)
128 e129e26b Kostas Papadimitriou
129 e129e26b Kostas Papadimitriou
    return wrapper
130 e129e26b Kostas Papadimitriou
131 e129e26b Kostas Papadimitriou
132 e129e26b Kostas Papadimitriou
@helpdesk_user_required
133 c3564ce9 Kostas Papadimitriou
def index(request):
134 c3564ce9 Kostas Papadimitriou
    """
135 c3564ce9 Kostas Papadimitriou
    Helpdesk index view.
136 c3564ce9 Kostas Papadimitriou
    """
137 e129e26b Kostas Papadimitriou
138 c3564ce9 Kostas Papadimitriou
    # if form submitted redirect to details
139 c3564ce9 Kostas Papadimitriou
    account = request.GET.get('account', None)
140 c3564ce9 Kostas Papadimitriou
    if account:
141 d59d86d4 Kostas Papadimitriou
        return redirect('synnefo.helpdesk.views.account', account_or_ip=account)
142 c3564ce9 Kostas Papadimitriou
143 c3564ce9 Kostas Papadimitriou
    # show index template
144 c3564ce9 Kostas Papadimitriou
    return direct_to_template(request, "helpdesk/index.html")
145 c3564ce9 Kostas Papadimitriou
146 c3564ce9 Kostas Papadimitriou
147 e129e26b Kostas Papadimitriou
@helpdesk_user_required
148 d59d86d4 Kostas Papadimitriou
def account(request, account_or_ip):
149 c3564ce9 Kostas Papadimitriou
    """
150 e129e26b Kostas Papadimitriou
    Account details view.
151 c3564ce9 Kostas Papadimitriou
    """
152 e129e26b Kostas Papadimitriou
153 605d23bf Kostas Papadimitriou
    show_deleted = bool(int(request.GET.get('deleted', SHOW_DELETED_VMS)))
154 605d23bf Kostas Papadimitriou
155 9e3519e0 Kostas Papadimitriou
    account_exists = True
156 d59d86d4 Kostas Papadimitriou
    vms = []
157 d59d86d4 Kostas Papadimitriou
    networks = []
158 d59d86d4 Kostas Papadimitriou
    is_ip = IP_SEARCH_REGEX.match(account_or_ip)
159 d59d86d4 Kostas Papadimitriou
    account = account_or_ip
160 d59d86d4 Kostas Papadimitriou
161 d59d86d4 Kostas Papadimitriou
    if is_ip:
162 d59d86d4 Kostas Papadimitriou
        try:
163 d59d86d4 Kostas Papadimitriou
            nic = NetworkInterface.objects.get(ipv4=account_or_ip)
164 d59d86d4 Kostas Papadimitriou
            account = nic.machine.userid
165 d59d86d4 Kostas Papadimitriou
        except NetworkInterface.DoesNotExist:
166 d59d86d4 Kostas Papadimitriou
            account_exists = False
167 d59d86d4 Kostas Papadimitriou
    else:
168 605d23bf Kostas Papadimitriou
        filter_extra = {}
169 605d23bf Kostas Papadimitriou
        if not show_deleted:
170 605d23bf Kostas Papadimitriou
            filter_extra['deleted'] = False
171 d59d86d4 Kostas Papadimitriou
172 605d23bf Kostas Papadimitriou
        # all user vms
173 605d23bf Kostas Papadimitriou
        vms = VirtualMachine.objects.filter(userid=account,
174 605d23bf Kostas Papadimitriou
                                            **filter_extra).order_by('deleted')
175 d59d86d4 Kostas Papadimitriou
        # return all user private and public networks
176 605d23bf Kostas Papadimitriou
        public_networks = Network.objects.filter(public=True,
177 605d23bf Kostas Papadimitriou
                                                 **filter_extra).order_by('state')
178 605d23bf Kostas Papadimitriou
        private_networks = Network.objects.filter(userid=account,
179 605d23bf Kostas Papadimitriou
                                                 **filter_extra).order_by('state')
180 d59d86d4 Kostas Papadimitriou
        networks = list(public_networks) + list(private_networks)
181 d59d86d4 Kostas Papadimitriou
182 d59d86d4 Kostas Papadimitriou
        if vms.count() == 0 and private_networks.count() == 0:
183 d59d86d4 Kostas Papadimitriou
            account_exists = False
184 e129e26b Kostas Papadimitriou
185 c3564ce9 Kostas Papadimitriou
    user_context = {
186 9e3519e0 Kostas Papadimitriou
        'account_exists': account_exists,
187 d59d86d4 Kostas Papadimitriou
        'is_ip': is_ip,
188 c3564ce9 Kostas Papadimitriou
        'account': account,
189 9e3519e0 Kostas Papadimitriou
        'vms': vms,
190 605d23bf Kostas Papadimitriou
        'csrf_token': request.user['auth_token'],
191 c3564ce9 Kostas Papadimitriou
        'networks': networks,
192 e620ca07 Olga Brani
        'UI_MEDIA_URL': settings.UI_MEDIA_URL
193 c3564ce9 Kostas Papadimitriou
    }
194 d59d86d4 Kostas Papadimitriou
195 c3564ce9 Kostas Papadimitriou
    return direct_to_template(request, "helpdesk/account.html",
196 c3564ce9 Kostas Papadimitriou
        extra_context=user_context)
197 c3564ce9 Kostas Papadimitriou
198 ebda2ad4 Kostas Papadimitriou
199 9e3519e0 Kostas Papadimitriou
@helpdesk_user_required
200 605d23bf Kostas Papadimitriou
@token_check
201 605d23bf Kostas Papadimitriou
def suspend_vm(request, vm_id):
202 605d23bf Kostas Papadimitriou
    vm = VirtualMachine.objects.get(pk=vm_id)
203 605d23bf Kostas Papadimitriou
    vm.suspended = True
204 605d23bf Kostas Papadimitriou
    vm.save()
205 605d23bf Kostas Papadimitriou
    account = vm.userid
206 605d23bf Kostas Papadimitriou
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))
207 605d23bf Kostas Papadimitriou
208 605d23bf Kostas Papadimitriou
209 605d23bf Kostas Papadimitriou
@helpdesk_user_required
210 605d23bf Kostas Papadimitriou
@token_check
211 605d23bf Kostas Papadimitriou
def suspend_vm_release(request, vm_id):
212 605d23bf Kostas Papadimitriou
    vm = VirtualMachine.objects.get(pk=vm_id)
213 605d23bf Kostas Papadimitriou
    vm.suspended = False
214 605d23bf Kostas Papadimitriou
    vm.save()
215 605d23bf Kostas Papadimitriou
    account = vm.userid
216 605d23bf Kostas Papadimitriou
    return HttpResponseRedirect(reverse('helpdesk-details', args=(account,)))
217 605d23bf Kostas Papadimitriou
218 605d23bf Kostas Papadimitriou
219 605d23bf Kostas Papadimitriou
@helpdesk_user_required
220 9e3519e0 Kostas Papadimitriou
def user_list(request):
221 9e3519e0 Kostas Papadimitriou
    """
222 9e3519e0 Kostas Papadimitriou
    Return a json list of users based on the prefix provided. Prefix
223 9e3519e0 Kostas Papadimitriou
    should end with "@".
224 9e3519e0 Kostas Papadimitriou
    """
225 9e3519e0 Kostas Papadimitriou
226 9e3519e0 Kostas Papadimitriou
    prefix = request.GET.get('prefix', None)
227 ebda2ad4 Kostas Papadimitriou
    if not prefix or "@" not in prefix:
228 9e3519e0 Kostas Papadimitriou
        raise Http404
229 9e3519e0 Kostas Papadimitriou
230 ebda2ad4 Kostas Papadimitriou
    # keep only the user part (e.g. "user@")
231 ebda2ad4 Kostas Papadimitriou
    prefix = prefix.split("@")[0] + "@"
232 ebda2ad4 Kostas Papadimitriou
233 9e3519e0 Kostas Papadimitriou
    q = Q(userid__startswith=prefix) & ~Q(userid=None)
234 9e3519e0 Kostas Papadimitriou
    vm_users = VirtualMachine.objects.filter(q).values_list("userid", flat=True)
235 9e3519e0 Kostas Papadimitriou
    net_users = Network.objects.filter(q).values_list("userid", flat=True)
236 9e3519e0 Kostas Papadimitriou
    users = list(set(list(vm_users) + list(net_users)))
237 9e3519e0 Kostas Papadimitriou
    return HttpResponse(json.dumps(users), content_type="application/json")