root / pithos / im / views.py @ 32e87ed2
History | View | Annotate | Download (8.1 kB)
1 |
# Copyright 2011 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 json |
35 |
import logging |
36 |
import socket |
37 |
|
38 |
from datetime import datetime |
39 |
from functools import wraps |
40 |
from math import ceil |
41 |
from random import randint |
42 |
from smtplib import SMTPException |
43 |
|
44 |
from django.conf import settings |
45 |
from django.core.mail import send_mail |
46 |
from django.http import HttpResponse, HttpResponseRedirect |
47 |
from django.shortcuts import redirect |
48 |
from django.template.loader import render_to_string |
49 |
from django.utils.http import urlencode |
50 |
from django.utils.translation import ugettext as _ |
51 |
|
52 |
from pithos.im.models import User, Invitation |
53 |
from pithos.im.util import isoformat |
54 |
|
55 |
|
56 |
def render_response(template, tab=None, status=200, **kwargs): |
57 |
if tab is None: |
58 |
tab = template.partition('_')[0] |
59 |
kwargs.setdefault('tab', tab)
|
60 |
html = render_to_string(template, kwargs) |
61 |
return HttpResponse(html, status=status)
|
62 |
|
63 |
|
64 |
def requires_login(func): |
65 |
@wraps(func)
|
66 |
def wrapper(request, *args): |
67 |
if not settings.BYPASS_ADMIN_AUTH: |
68 |
if not request.user: |
69 |
next = urlencode({'next': request.build_absolute_uri()})
|
70 |
login_uri = settings.LOGIN_URL + '?' + next |
71 |
return HttpResponseRedirect(login_uri)
|
72 |
return func(request, *args)
|
73 |
return wrapper
|
74 |
|
75 |
|
76 |
def requires_admin(func): |
77 |
@wraps(func)
|
78 |
def wrapper(request, *args): |
79 |
if not settings.BYPASS_ADMIN_AUTH: |
80 |
if not request.user: |
81 |
next = urlencode({'next': request.build_absolute_uri()})
|
82 |
login_uri = settings.LOGIN_URL + '?' + next |
83 |
return HttpResponseRedirect(login_uri)
|
84 |
if not request.user_obj.is_admin: |
85 |
return HttpResponse('Forbidden', status=403) |
86 |
return func(request, *args)
|
87 |
return wrapper
|
88 |
|
89 |
|
90 |
def index(request): |
91 |
# TODO: Get and pass on next variable.
|
92 |
return render_response('index.html') |
93 |
|
94 |
|
95 |
@requires_admin
|
96 |
def admin(request): |
97 |
stats = {} |
98 |
stats['users'] = User.objects.count()
|
99 |
|
100 |
invitations = Invitation.objects.all() |
101 |
stats['invitations'] = invitations.count()
|
102 |
stats['invitations_accepted'] = invitations.filter(is_accepted=True).count() |
103 |
|
104 |
return render_response('admin.html', tab='home', stats=stats) |
105 |
|
106 |
|
107 |
@requires_admin
|
108 |
def users_list(request): |
109 |
users = User.objects.order_by('id')
|
110 |
|
111 |
filter = request.GET.get('filter', '') |
112 |
if filter: |
113 |
if filter.startswith('-'): |
114 |
users = users.exclude(uniq__icontains=filter[1:]) |
115 |
else:
|
116 |
users = users.filter(uniq__icontains=filter)
|
117 |
|
118 |
try:
|
119 |
page = int(request.GET.get('page', 1)) |
120 |
except ValueError: |
121 |
page = 1
|
122 |
offset = max(0, page - 1) * settings.ADMIN_PAGE_LIMIT |
123 |
limit = offset + settings.ADMIN_PAGE_LIMIT |
124 |
|
125 |
npages = int(ceil(1.0 * users.count() / settings.ADMIN_PAGE_LIMIT)) |
126 |
prev = page - 1 if page > 1 else None |
127 |
next = page + 1 if page < npages else None |
128 |
return render_response('users_list.html', |
129 |
users=users[offset:limit], |
130 |
filter=filter,
|
131 |
pages=range(1, npages + 1), |
132 |
page=page, |
133 |
prev=prev, |
134 |
next=next)
|
135 |
|
136 |
|
137 |
@requires_admin
|
138 |
def users_create(request): |
139 |
if request.method == 'GET': |
140 |
return render_response('users_create.html') |
141 |
if request.method == 'POST': |
142 |
user = User() |
143 |
user.uniq = request.POST.get('uniq')
|
144 |
user.realname = request.POST.get('realname')
|
145 |
user.is_admin = True if request.POST.get('admin') else False |
146 |
user.affiliation = request.POST.get('affiliation')
|
147 |
user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3) # In GiB |
148 |
user.renew_token() |
149 |
user.save() |
150 |
return redirect(users_info, user.id)
|
151 |
|
152 |
|
153 |
@requires_admin
|
154 |
def users_info(request, user_id): |
155 |
user = User.objects.get(id=user_id) |
156 |
states = [x[0] for x in User.ACCOUNT_STATE] |
157 |
return render_response('users_info.html', |
158 |
user=user, |
159 |
states=states) |
160 |
|
161 |
|
162 |
@requires_admin
|
163 |
def users_modify(request, user_id): |
164 |
user = User.objects.get(id=user_id) |
165 |
user.uniq = request.POST.get('uniq')
|
166 |
user.realname = request.POST.get('realname')
|
167 |
user.is_admin = True if request.POST.get('admin') else False |
168 |
user.affiliation = request.POST.get('affiliation')
|
169 |
user.state = request.POST.get('state')
|
170 |
user.invitations = int(request.POST.get('invitations') or 0) |
171 |
user.quota = int(request.POST.get('quota') or 0) * (1024 ** 3) # In GiB |
172 |
user.auth_token = request.POST.get('auth_token')
|
173 |
try:
|
174 |
auth_token_expires = request.POST.get('auth_token_expires')
|
175 |
d = datetime.strptime(auth_token_expires, '%Y-%m-%dT%H:%MZ')
|
176 |
user.auth_token_expires = d |
177 |
except ValueError: |
178 |
pass
|
179 |
user.save() |
180 |
return redirect(users_info, user.id)
|
181 |
|
182 |
|
183 |
@requires_admin
|
184 |
def users_delete(request, user_id): |
185 |
user = User.objects.get(id=user_id) |
186 |
user.delete() |
187 |
return redirect(users_list)
|
188 |
|
189 |
|
190 |
def generate_invitation_code(): |
191 |
return randint(1, 2L**63 - 1) |
192 |
|
193 |
|
194 |
def send_invitation(inv): |
195 |
url = settings.INVITATION_LOGIN_TARGET % inv.code |
196 |
subject = _('Invitation to Pithos')
|
197 |
message = render_to_string('invitation.txt', {
|
198 |
'invitation': inv,
|
199 |
'url': url})
|
200 |
sender = settings.DEFAULT_FROM_EMAIL |
201 |
send_mail(subject, message, sender, [inv.uniq]) |
202 |
inv.inviter.invitations = max(0, inv.inviter.invitations - 1) |
203 |
inv.inviter.save() |
204 |
logging.info('Sent invitation %s', inv)
|
205 |
|
206 |
|
207 |
@requires_login
|
208 |
def invite(request): |
209 |
status = None
|
210 |
message = None
|
211 |
|
212 |
if request.method == 'POST': |
213 |
if request.user_obj.invitations > 0: |
214 |
code = generate_invitation_code() |
215 |
invitation, created = Invitation.objects.get_or_create(code=code) |
216 |
invitation.inviter=request.user_obj |
217 |
invitation.realname=request.POST.get('realname')
|
218 |
invitation.uniq=request.POST.get('uniq')
|
219 |
invitation.save() |
220 |
|
221 |
try:
|
222 |
send_invitation(invitation) |
223 |
status = 'success'
|
224 |
message = _('Invitation sent to %s' % invitation.uniq)
|
225 |
except (SMTPException, socket.error) as e: |
226 |
status = 'error'
|
227 |
message = e.strerror |
228 |
else:
|
229 |
status = 'error'
|
230 |
message = _('No invitations left')
|
231 |
|
232 |
if request.GET.get('format') == 'json': |
233 |
rep = {'invitations': request.user_obj.invitations}
|
234 |
return HttpResponse(json.dumps(rep))
|
235 |
|
236 |
html = render_to_string('invitations.html', {
|
237 |
'user': request.user_obj,
|
238 |
'status': status,
|
239 |
'message': message})
|
240 |
return HttpResponse(html)
|